From 9fcec107373b6846f3775fe353c7ae29653cf9ac Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 26 Oct 2015 23:23:03 +0100 Subject: [PATCH] Finished porting LayerRegion to C++ --- lib/Slic3r.pm | 2 - lib/Slic3r/Fill.pm | 30 ++--- lib/Slic3r/Layer.pm | 25 ++-- lib/Slic3r/Layer/Region.pm | 103 ---------------- lib/Slic3r/Print/GCode.pm | 2 +- lib/Slic3r/Print/Object.pm | 25 ++-- lib/Slic3r/Print/SupportMaterial.pm | 4 +- xs/src/libslic3r/BridgeDetector.cpp | 36 +++--- xs/src/libslic3r/BridgeDetector.hpp | 4 +- xs/src/libslic3r/ClipperUtils.cpp | 22 +++- xs/src/libslic3r/ClipperUtils.hpp | 8 +- xs/src/libslic3r/ExPolygonCollection.cpp | 6 + xs/src/libslic3r/ExPolygonCollection.hpp | 1 + xs/src/libslic3r/Layer.hpp | 5 +- xs/src/libslic3r/LayerRegion.cpp | 148 +++++++++++++++++++++++ xs/src/libslic3r/PerimeterGenerator.cpp | 24 ++-- xs/src/libslic3r/PolylineCollection.cpp | 6 + xs/src/libslic3r/PolylineCollection.hpp | 1 + xs/src/libslic3r/Print.hpp | 1 + xs/src/libslic3r/PrintObject.cpp | 34 ++++-- xs/src/libslic3r/Surface.cpp | 5 + xs/src/libslic3r/Surface.hpp | 1 + xs/src/libslic3r/SurfaceCollection.cpp | 6 + xs/src/libslic3r/SurfaceCollection.hpp | 4 + xs/src/libslic3r/libslic3r.h | 1 + xs/xsp/BridgeDetector.xsp | 12 +- xs/xsp/Layer.xsp | 5 +- xs/xsp/Print.xsp | 1 + 28 files changed, 319 insertions(+), 203 deletions(-) delete mode 100644 lib/Slic3r/Layer/Region.pm diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index bcd337dec7..2668f7229d 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -54,7 +54,6 @@ use Slic3r::GCode::VibrationLimit; use Slic3r::Geometry qw(PI); use Slic3r::Geometry::Clipper; use Slic3r::Layer; -use Slic3r::Layer::Region; use Slic3r::Line; use Slic3r::Model; use Slic3r::Point; @@ -77,7 +76,6 @@ use constant RESOLUTION => 0.0125; use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15; use constant INFILL_OVERLAP_OVER_SPACING => 0.3; -use constant EXTERNAL_INFILL_MARGIN => 3; # keep track of threads we created my @my_threads = (); diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index a39b77fc4b..88672a055b 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -48,9 +48,9 @@ sub make_fill { my $self = shift; my ($layerm) = @_; - Slic3r::debugf "Filling layer %d:\n", $layerm->id; + Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; - my $fill_density = $layerm->config->fill_density; + my $fill_density = $layerm->region->config->fill_density; my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL); my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL); my $top_solid_infill_flow = $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL); @@ -74,13 +74,13 @@ sub make_fill { for (my $i = 0; $i <= $#groups; $i++) { # we can only merge solid non-bridge surfaces, so discard # non-solid surfaces - if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->id == 0)) { + if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->layer->id == 0)) { $is_solid[$i] = 1; $fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP) ? $top_solid_infill_flow->width : $solid_infill_flow->width; $pattern[$i] = $groups[$i][0]->is_external - ? $layerm->config->external_fill_pattern + ? $layerm->region->config->external_fill_pattern : 'rectilinear'; } else { $is_solid[$i] = 0; @@ -179,19 +179,19 @@ sub make_fill { my @fills = (); SURFACE: foreach my $surface (@surfaces) { next if $surface->surface_type == S_TYPE_INTERNALVOID; - my $filler = $layerm->config->fill_pattern; + my $filler = $layerm->region->config->fill_pattern; my $density = $fill_density; my $role = ($surface->surface_type == S_TYPE_TOP) ? FLOW_ROLE_TOP_SOLID_INFILL : $surface->is_solid ? FLOW_ROLE_SOLID_INFILL : FLOW_ROLE_INFILL; - my $is_bridge = $layerm->id > 0 && $surface->is_bridge; + my $is_bridge = $layerm->layer->id > 0 && $surface->is_bridge; my $is_solid = $surface->is_solid; if ($surface->is_solid) { $density = 100; $filler = 'rectilinear'; if ($surface->is_external && !$is_bridge) { - $filler = $layerm->config->external_fill_pattern; + $filler = $layerm->region->config->external_fill_pattern; } } else { next SURFACE unless $density > 0; @@ -201,14 +201,14 @@ sub make_fill { my $f = $self->filler($filler); # calculate the actual flow we'll be using for this infill - my $h = $surface->thickness == -1 ? $layerm->height : $surface->thickness; + my $h = $surface->thickness == -1 ? $layerm->layer->height : $surface->thickness; my $flow = $layerm->region->flow( $role, $h, $is_bridge || $f->use_bridge_flow, - $layerm->id == 0, + $layerm->layer->id == 0, -1, - $layerm->object, + $layerm->layer->object, ); # calculate flow spacing for infill pattern generation @@ -220,11 +220,11 @@ sub make_fill { # layer height my $internal_flow = $layerm->region->flow( FLOW_ROLE_INFILL, - $layerm->object->config->layer_height, # TODO: handle infill_every_layers? + $layerm->layer->object->config->layer_height, # TODO: handle infill_every_layers? 0, # no bridge 0, # no first layer -1, # auto width - $layerm->object, + $layerm->layer->object, ); $f->spacing($internal_flow->spacing); $using_internal_flow = 1; @@ -232,9 +232,9 @@ sub make_fill { $f->spacing($flow->spacing); } - $f->layer_id($layerm->id); - $f->z($layerm->print_z); - $f->angle(deg2rad($layerm->config->fill_angle)); + $f->layer_id($layerm->layer->id); + $f->z($layerm->layer->print_z); + $f->angle(deg2rad($layerm->region->config->fill_angle)); $f->loop_clipping(scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); # apply half spacing using this flow's own spacing and generate infill diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 2e65c147e9..b16c743798 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -41,24 +41,25 @@ sub make_perimeters { for my $region_id (0..$#{$self->regions}) { next if $done{$region_id}; - my $layerm = $self->regions->[$region_id]; + my $layerm = $self->get_region($region_id); + my $config = $layerm->region->config; $done{$region_id} = 1; # find compatible regions my @layerms = ($layerm); for my $i (($region_id+1)..$#{$self->regions}) { - my $config = $self->regions->[$i]->config; - my $layerm_config = $layerm->config; + my $other_layerm = $self->get_region($i); + my $other_config = $other_layerm->region->config; - if ($config->perimeter_extruder == $layerm_config->perimeter_extruder - && $config->perimeters == $layerm_config->perimeters - && $config->perimeter_speed == $layerm_config->perimeter_speed - && $config->gap_fill_speed == $layerm_config->gap_fill_speed - && $config->overhangs == $layerm_config->overhangs - && $config->perimeter_extrusion_width == $layerm_config->perimeter_extrusion_width - && $config->thin_walls == $layerm_config->thin_walls - && $config->external_perimeters_first == $layerm_config->external_perimeters_first) { - push @layerms, $self->regions->[$i]; + if ($config->perimeter_extruder == $other_config->perimeter_extruder + && $config->perimeters == $other_config->perimeters + && $config->perimeter_speed == $other_config->perimeter_speed + && $config->gap_fill_speed == $other_config->gap_fill_speed + && $config->overhangs == $other_config->overhangs + && $config->perimeter_extrusion_width == $other_config->perimeter_extrusion_width + && $config->thin_walls == $other_config->thin_walls + && $config->external_perimeters_first == $other_config->external_perimeters_first) { + push @layerms, $other_layerm; $done{$i} = 1; } } diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm deleted file mode 100644 index 09e4a9fc81..0000000000 --- a/lib/Slic3r/Layer/Region.pm +++ /dev/null @@ -1,103 +0,0 @@ -package Slic3r::Layer::Region; -use strict; -use warnings; - -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Flow ':roles'; -use Slic3r::Geometry qw(scale); -use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex - ); -use Slic3r::Surface ':types'; - - -# TODO: lazy -sub infill_area_threshold { - my $self = shift; - return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2; -} - -sub id { return $_[0]->layer->id; } -sub slice_z { return $_[0]->layer->slice_z; } -sub print_z { return $_[0]->layer->print_z; } -sub height { return $_[0]->layer->height; } -sub object { return $_[0]->layer->object; } -sub print { return $_[0]->layer->print; } - -sub config { return $_[0]->region->config; } - -sub process_external_surfaces { - my ($self, $lower_layer) = @_; - - my @surfaces = @{$self->fill_surfaces}; - my $margin = scale &Slic3r::EXTERNAL_INFILL_MARGIN; - - my @bottom = (); - foreach my $surface (grep $_->is_bottom, @surfaces) { - my $grown = $surface->expolygon->offset_ex(+$margin); - - # detect bridge direction before merging grown surfaces otherwise adjacent bridges - # would get merged into a single one while they need different directions - # also, supply the original expolygon instead of the grown one, because in case - # of very thin (but still working) anchors, the grown expolygon would go beyond them - my $angle; - if ($lower_layer) { - my $bridge_detector = Slic3r::BridgeDetector->new( - $surface->expolygon, - $lower_layer->slices, - $self->flow(FLOW_ROLE_INFILL, $self->height, 1)->scaled_width, - ); - Slic3r::debugf "Processing bridge at layer %d:\n", $self->id; - $bridge_detector->detect_angle; - $angle = $bridge_detector->angle; - - if (defined $angle && $self->object->config->support_material) { - $self->bridged->append(Slic3r::ExPolygon->new($_)) - for @{ $bridge_detector->coverage_by_angle($angle) }; - $self->unsupported_bridge_edges->append($_) for @{ $bridge_detector->unsupported_edges }; - } - } - - push @bottom, map $surface->clone(expolygon => $_, bridge_angle => $angle), @$grown; - } - - my @top = (); - foreach my $surface (grep $_->surface_type == S_TYPE_TOP, @surfaces) { - # give priority to bottom surfaces - my $grown = diff_ex( - $surface->expolygon->offset(+$margin), - [ map $_->p, @bottom ], - ); - push @top, map $surface->clone(expolygon => $_), @$grown; - } - - # if we're slicing with no infill, we can't extend external surfaces - # over non-existent infill - my @fill_boundaries = $self->config->fill_density > 0 - ? @surfaces - : grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; - - # intersect the grown surfaces with the actual fill boundaries - my @new_surfaces = (); - foreach my $group (@{Slic3r::Surface::Collection->new(@top, @bottom)->group}) { - push @new_surfaces, - map $group->[0]->clone(expolygon => $_), - @{intersection_ex( - [ map $_->p, @$group ], - [ map $_->p, @fill_boundaries ], - 1, # to ensure adjacent expolygons are unified - )}; - } - - # subtract the new top surfaces from the other non-top surfaces and re-add them - my @other = grep $_->surface_type != S_TYPE_TOP && !$_->is_bottom, @surfaces; - foreach my $group (@{Slic3r::Surface::Collection->new(@other)->group}) { - push @new_surfaces, map $group->[0]->clone(expolygon => $_), @{diff_ex( - [ map $_->p, @$group ], - [ map $_->p, @new_surfaces ], - )}; - } - $self->fill_surfaces->clear; - $self->fill_surfaces->append($_) for @new_surfaces; -} - -1; diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index db900d6fc4..8db7e2d334 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -357,7 +357,7 @@ sub process_layer { $self->_spiral_vase->enable( ($layer->id > 0 || $self->print->config->brim_width == 0) && ($layer->id >= $self->print->config->skirt_height && !$self->print->has_infinite_skirt) - && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions}) + && !defined(first { $_->region->config->bottom_solid_layers > $layer->id } @{$layer->regions}) && !defined(first { $_->perimeters->items_count > 1 } @{$layer->regions}) && !defined(first { $_->fills->items_count > 0 } @{$layer->regions}) ); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 13f43df7ab..15094cb934 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -830,17 +830,6 @@ sub clip_fill_surfaces { } } -sub process_external_surfaces { - my ($self) = @_; - - for my $region_id (0 .. ($self->print->region_count-1)) { - $self->get_layer(0)->regions->[$region_id]->process_external_surfaces(undef); - for my $i (1 .. ($self->layer_count - 1)) { - $self->get_layer($i)->regions->[$region_id]->process_external_surfaces($self->get_layer($i-1)); - } - } -} - sub discover_horizontal_shells { my $self = shift; @@ -850,8 +839,8 @@ sub discover_horizontal_shells { for (my $i = 0; $i < $self->layer_count; $i++) { my $layerm = $self->get_layer($i)->regions->[$region_id]; - if ($layerm->config->solid_infill_every_layers && $layerm->config->fill_density > 0 - && ($i % $layerm->config->solid_infill_every_layers) == 0) { + if ($layerm->region->config->solid_infill_every_layers && $layerm->region->config->fill_density > 0 + && ($i % $layerm->region->config->solid_infill_every_layers) == 0) { $_->surface_type(S_TYPE_INTERNALSOLID) for @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}; } @@ -873,8 +862,8 @@ sub discover_horizontal_shells { Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom'; my $solid_layers = ($type == S_TYPE_TOP) - ? $layerm->config->top_solid_layers - : $layerm->config->bottom_solid_layers; + ? $layerm->region->config->top_solid_layers + : $layerm->region->config->bottom_solid_layers; NEIGHBOR: for (my $n = ($type == S_TYPE_TOP) ? $i-1 : $i+1; abs($n - $i) <= $solid_layers-1; ($type == S_TYPE_TOP) ? $n-- : $n++) { @@ -902,7 +891,7 @@ sub discover_horizontal_shells { ); next EXTERNAL if !@$new_internal_solid; - if ($layerm->config->fill_density == 0) { + if ($layerm->region->config->fill_density == 0) { # if we're printing a hollow object we discard any solid shell thinner # than a perimeter width, since it's probably just crossing a sloping wall # and it's not wanted in a hollow print even if it would make sense when @@ -1100,12 +1089,12 @@ sub combine_infill { )}; # apply surfaces back with adjusted depth to the uppermost layer - if ($layerm->id == $self->get_layer($layer_idx)->id) { + 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 $_->height, @layerms), + thickness => sum(map $_->layer->height, @layerms), thickness_layers => scalar(@layerms), ), @$intersection; diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 0a18ebea29..eea6397afa 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -235,7 +235,7 @@ sub contact_area { # just remove bridged areas $diff = diff( $diff, - [ map @$_, @{$layerm->bridged} ], + $layerm->bridged, 1, ); } @@ -267,7 +267,7 @@ sub contact_area { # get the average nozzle diameter used on this layer my @nozzle_diameters = map $self->print_config->get_at('nozzle_diameter', $_), map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1, $_->config->solid_infill_extruder-1 } - @{$layer->regions}; + map $_->region, @{$layer->regions}; my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; my $contact_z = $layer->print_z - $self->contact_distance($layer->height, $nozzle_diameter); diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp index da59a04d88..1dddf81ce0 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/xs/src/libslic3r/BridgeDetector.cpp @@ -185,16 +185,12 @@ BridgeDetector::detect_angle() return true; } -void -BridgeDetector::coverage(Polygons* coverage) const -{ - if (this->angle == -1) return; - return this->coverage(angle, coverage); -} - void BridgeDetector::coverage(double angle, Polygons* coverage) const { + if (angle == -1) angle = this->angle; + if (angle == -1) return; + // Clone our expolygon and rotate it so that we work with vertical lines. ExPolygon expolygon = this->expolygon; expolygon.rotate(PI/2.0 - angle, Point(0,0)); @@ -263,19 +259,23 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const */ } -/* This method returns the bridge edges (as polylines) that are not supported - but would allow the entire bridge area to be bridged with detected angle - if supported too */ -void -BridgeDetector::unsupported_edges(Polylines* unsupported) const +Polygons +BridgeDetector::coverage(double angle) const { - if (this->angle == -1) return; - return this->unsupported_edges(this->angle, unsupported); + Polygons pp; + this->coverage(angle, &pp); + return pp; } +/* This method returns the bridge edges (as polylines) that are not supported + but would allow the entire bridge area to be bridged with detected angle + if supported too */ void BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const { + if (angle == -1) angle = this->angle; + if (angle == -1) return; + // get bridge edges (both contour and holes) Polylines bridge_edges; { @@ -319,6 +319,14 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const */ } +Polylines +BridgeDetector::unsupported_edges(double angle) const +{ + Polylines pp; + this->unsupported_edges(angle, &pp); + return pp; +} + #ifdef SLIC3RXS REGISTER_CLASS(BridgeDetector, "BridgeDetector"); #endif diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index ab25712895..c3af095429 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -18,10 +18,10 @@ class BridgeDetector { BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); bool detect_angle(); - void coverage(Polygons* coverage) const; void coverage(double angle, Polygons* coverage) const; - void unsupported_edges(Polylines* unsupported) const; + Polygons coverage(double angle = -1) const; void unsupported_edges(double angle, Polylines* unsupported) const; + Polylines unsupported_edges(double angle = -1) const; private: Polylines _edges; // representing the supporting edges diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index baddd8f456..3d07891b10 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -212,6 +212,15 @@ offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float ClipperPaths_to_Slic3rExPolygons(output, retval); } +Slic3r::ExPolygons +offset_ex(const Slic3r::Polygons &polygons, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + Slic3r::ExPolygons expp; + offset(polygons, &expp, delta, scale, joinType, miterLimit); + return expp; +} + void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) @@ -472,13 +481,16 @@ diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_ return pp; } +template Slic3r::ExPolygons -diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) +diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_) { Slic3r::ExPolygons expp; diff(subject, clip, &expp, safety_offset_); return expp; } +template Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); +template Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_); template void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_) @@ -507,6 +519,14 @@ intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, boo return pp; } +Slic3r::ExPolygons +intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) +{ + Slic3r::ExPolygons expp; + intersection(subject, clip, &expp, safety_offset_); + return expp; +} + template bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) { diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 4a3ba2e5c4..2b4e8c0b34 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -58,6 +58,9 @@ void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const floa void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); +Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, @@ -96,13 +99,16 @@ template void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false); Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); -Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); + +template +Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false); template void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); Slic3r::Polylines intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); template bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp index 9ae7e89079..45f0df477d 100644 --- a/xs/src/libslic3r/ExPolygonCollection.cpp +++ b/xs/src/libslic3r/ExPolygonCollection.cpp @@ -122,6 +122,12 @@ ExPolygonCollection::contours() const return contours; } +void +ExPolygonCollection::append(const ExPolygons &expp) +{ + this->expolygons.insert(this->expolygons.end(), expp.begin(), expp.end()); +} + #ifdef SLIC3RXS REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection"); #endif diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/xs/src/libslic3r/ExPolygonCollection.hpp index 4e6c366e29..ffb306c927 100644 --- a/xs/src/libslic3r/ExPolygonCollection.hpp +++ b/xs/src/libslic3r/ExPolygonCollection.hpp @@ -31,6 +31,7 @@ class ExPolygonCollection Polygon convex_hull() const; Lines lines() const; Polygons contours() const; + void append(const ExPolygons &expolygons); }; } diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index 1c09b5f0f0..7adaa0fbb6 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -40,8 +40,7 @@ class LayerRegion // collection of expolygons representing the bridged areas (thus not // needing support material) - // (this could be just a Polygons object) - ExPolygonCollection bridged; + Polygons bridged; // collection of polylines representing the unsupported bridge edges PolylineCollection unsupported_bridge_edges; @@ -58,6 +57,8 @@ class LayerRegion void merge_slices(); void prepare_fill_surfaces(); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); + void process_external_surfaces(const Layer* lower_layer); + double infill_area_threshold() const; private: Layer *_layer; diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index ef6150015e..4f874a2ead 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -1,4 +1,5 @@ #include "Layer.hpp" +#include "BridgeDetector.hpp" #include "ClipperUtils.hpp" #include "PerimeterGenerator.hpp" #include "Print.hpp" @@ -86,6 +87,146 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* g.process(); } +void +LayerRegion::process_external_surfaces(const Layer* lower_layer) +{ + const Surfaces &surfaces = this->fill_surfaces.surfaces; + const double margin = scale_(EXTERNAL_INFILL_MARGIN); + + SurfaceCollection bottom; + for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { + if (!surface->is_bottom()) continue; + + ExPolygons grown = offset_ex(surface->expolygon, +margin); + + /* detect bridge direction before merging grown surfaces otherwise adjacent bridges + would get merged into a single one while they need different directions + also, supply the original expolygon instead of the grown one, because in case + of very thin (but still working) anchors, the grown expolygon would go beyond them */ + double angle = -1; + if (lower_layer != NULL) { + BridgeDetector bd( + surface->expolygon, + lower_layer->slices, + this->flow(frInfill, this->layer()->height, true).scaled_width() + ); + + #ifdef SLIC3R_DEBUG + printf("Processing bridge at layer %zu:\n", this->layer()->id(); + #endif + + if (bd.detect_angle() && this->layer()->object()->config.support_material) { + angle = bd.angle; + + Polygons coverage = bd.coverage(); + this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end()); + this->unsupported_bridge_edges.append(bd.unsupported_edges()); + } + } + + for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) { + Surface s = *surface; + s.expolygon = *it; + s.bridge_angle = angle; + bottom.surfaces.push_back(s); + } + } + + SurfaceCollection top; + for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { + if (surface->surface_type != stTop) continue; + + // give priority to bottom surfaces + ExPolygons grown = diff_ex( + offset(surface->expolygon, +margin), + (Polygons)bottom + ); + for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) { + Surface s = *surface; + s.expolygon = *it; + top.surfaces.push_back(s); + } + } + + /* if we're slicing with no infill, we can't extend external surfaces + over non-existent infill */ + SurfaceCollection fill_boundaries; + if (this->region()->config.fill_density.value > 0) { + fill_boundaries = SurfaceCollection(surfaces); + } else { + for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) { + if (it->surface_type != stInternal) + fill_boundaries.surfaces.push_back(*it); + } + } + + // intersect the grown surfaces with the actual fill boundaries + SurfaceCollection new_surfaces; + { + // merge top and bottom in a single collection + SurfaceCollection tb = top; + tb.surfaces.insert(tb.surfaces.end(), bottom.surfaces.begin(), bottom.surfaces.end()); + + // group surfaces + std::vector groups; + tb.group(&groups); + + for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { + Polygons subject; + for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) { + Polygons pp = **s; + subject.insert(subject.end(), pp.begin(), pp.end()); + } + + ExPolygons expp = intersection_ex( + subject, + (Polygons)fill_boundaries, + true // to ensure adjacent expolygons are unified + ); + + for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { + Surface s = *g->front(); + s.expolygon = *ex; + new_surfaces.surfaces.push_back(s); + } + } + } + + /* subtract the new top surfaces from the other non-top surfaces and re-add them */ + { + SurfaceCollection other; + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + if (s->surface_type != stTop && !s->is_bottom()) + other.surfaces.push_back(*s); + } + + // group surfaces + std::vector groups; + other.group(&groups); + + for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { + Polygons subject; + for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) { + Polygons pp = **s; + subject.insert(subject.end(), pp.begin(), pp.end()); + } + + ExPolygons expp = diff_ex( + subject, + (Polygons)new_surfaces + ); + + for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { + Surface s = *g->front(); + s.expolygon = *ex; + new_surfaces.surfaces.push_back(s); + } + } + } + + this->fill_surfaces = new_surfaces; +} + void LayerRegion::prepare_fill_surfaces() { @@ -123,6 +264,13 @@ LayerRegion::prepare_fill_surfaces() } } +double +LayerRegion::infill_area_threshold() const +{ + double ss = this->flow(frSolidInfill).scaled_spacing(); + return ss*ss; +} + #ifdef SLIC3RXS REGISTER_CLASS(LayerRegion, "Layer::Region"); #endif diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index a968485318..32662f59c3 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -47,7 +47,7 @@ PerimeterGenerator::process() for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); surface != this->slices->surfaces.end(); ++surface) { // detect how many perimeters must be generated for this island - short loop_number = this->config->perimeters + surface->extra_perimeters; + signed short loop_number = this->config->perimeters + surface->extra_perimeters; loop_number--; // 0-indexed loops Polygons gaps; @@ -60,7 +60,7 @@ PerimeterGenerator::process() Polylines thin_walls; // we loop one time more than needed in order to find gaps after the last perimeter was applied - for (unsigned short i = 0; i <= loop_number+1; ++i) { // outer loop is 0 + for (signed short i = 0; i <= loop_number+1; ++i) { // outer loop is 0 Polygons offsets; if (i == 0) { // the minimum thickness of a single loop is: @@ -166,16 +166,16 @@ PerimeterGenerator::process() } // nest loops: holes first - for (unsigned short d = 0; d <= loop_number; ++d) { + for (signed short d = 0; d <= loop_number; ++d) { PerimeterGeneratorLoops &holes_d = holes[d]; // loop through all holes having depth == d - for (unsigned short i = 0; i < holes_d.size(); ++i) { + for (signed short i = 0; i < holes_d.size(); ++i) { const PerimeterGeneratorLoop &loop = holes_d[i]; // find the hole loop that contains this one, if any - for (unsigned short t = d+1; t <= loop_number; ++t) { - for (unsigned short j = 0; j < holes[t].size(); ++j) { + for (signed short t = d+1; t <= loop_number; ++t) { + for (signed short j = 0; j < holes[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = holes[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -187,8 +187,8 @@ PerimeterGenerator::process() } // if no hole contains this hole, find the contour loop that contains it - for (short t = loop_number; t >= 0; --t) { - for (unsigned short j = 0; j < contours[t].size(); ++j) { + for (signed short t = loop_number; t >= 0; --t) { + for (signed short j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -203,16 +203,16 @@ PerimeterGenerator::process() } // nest contour loops - for (short d = loop_number; d >= 1; --d) { + for (signed short d = loop_number; d >= 1; --d) { PerimeterGeneratorLoops &contours_d = contours[d]; // loop through all contours having depth == d - for (unsigned short i = 0; i < contours_d.size(); ++i) { + for (signed short i = 0; i < contours_d.size(); ++i) { const PerimeterGeneratorLoop &loop = contours_d[i]; // find the contour loop that contains it - for (short t = d-1; t >= 0; --t) { - for (unsigned short j = 0; j < contours[t].size(); ++j) { + for (signed short t = d-1; t >= 0; --t) { + for (signed short j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/xs/src/libslic3r/PolylineCollection.cpp index c2a142cf31..b7e34b6705 100644 --- a/xs/src/libslic3r/PolylineCollection.cpp +++ b/xs/src/libslic3r/PolylineCollection.cpp @@ -50,6 +50,12 @@ PolylineCollection::leftmost_point() const return p; } +void +PolylineCollection::append(const Polylines &pp) +{ + this->polylines.insert(this->polylines.end(), pp.begin(), pp.end()); +} + #ifdef SLIC3RXS REGISTER_CLASS(PolylineCollection, "Polyline::Collection"); #endif diff --git a/xs/src/libslic3r/PolylineCollection.hpp b/xs/src/libslic3r/PolylineCollection.hpp index ace03ad378..d903b35c58 100644 --- a/xs/src/libslic3r/PolylineCollection.hpp +++ b/xs/src/libslic3r/PolylineCollection.hpp @@ -13,6 +13,7 @@ class PolylineCollection void chained_path(PolylineCollection* retval, bool no_reverse = false) const; void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const; Point leftmost_point() const; + void append(const Polylines &polylines); }; } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 9c8c1738f0..c88dbbda9f 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -135,6 +135,7 @@ class PrintObject bool invalidate_all_steps(); bool has_support_material() const; + void process_external_surfaces(); void bridge_over_infill(); private: diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 7c184c2580..a405e320d0 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -340,14 +340,32 @@ PrintObject::has_support_material() const || this->config.support_material_enforce_layers > 0; } +void +PrintObject::process_external_surfaces() +{ + FOREACH_REGION(this->_print, region) { + size_t region_id = region - this->_print->regions.begin(); + + FOREACH_LAYER(this, layer_it) { + const Layer* lower_layer = (layer_it == this->layers.begin()) + ? NULL + : *(layer_it-1); + + (*layer_it)->get_region(region_id)->process_external_surfaces(lower_layer); + } + } +} + +/* This method applies bridge flow to the first internal solid layer above + sparse infill */ void PrintObject::bridge_over_infill() { FOREACH_REGION(this->_print, region) { size_t region_id = region - this->_print->regions.begin(); - double fill_density = (*region)->config.fill_density.value; - if (fill_density == 100) continue; + // skip bridging in case there are no voids + if ((*region)->config.fill_density.value == 100) continue; // get bridge flow Flow bridge_flow = (*region)->flow( @@ -360,6 +378,7 @@ PrintObject::bridge_over_infill() ); FOREACH_LAYER(this, layer_it) { + // skip first layer if (layer_it == this->layers.begin()) continue; Layer* layer = *layer_it; @@ -379,7 +398,7 @@ PrintObject::bridge_over_infill() // iterate through lower layers spanned by bridge_flow double bottom_z = layer->print_z - bridge_flow.height; for (int i = (layer_it - this->layers.begin()) - 1; i >= 0; --i) { - Layer* lower_layer = this->layers[i]; + const Layer* lower_layer = this->layers[i]; // stop iterating if layer is lower than bottom_z if (lower_layer->print_z < bottom_z) break; @@ -390,19 +409,19 @@ PrintObject::bridge_over_infill() (*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal); // intersect such lower internal surfaces with the candidate solid surfaces - intersection(to_bridge_pp, lower_internal, &to_bridge_pp); + to_bridge_pp = intersection(to_bridge_pp, lower_internal); } // there's no point in bridging too thin/short regions { double min_width = bridge_flow.scaled_width() * 3; - offset2(to_bridge_pp, &to_bridge_pp, -min_width, +min_width); + to_bridge_pp = offset2(to_bridge_pp, -min_width, +min_width); } if (to_bridge_pp.empty()) continue; // convert into ExPolygons - union_(to_bridge_pp, &to_bridge); + to_bridge = union_ex(to_bridge_pp); } #ifdef SLIC3R_DEBUG @@ -410,8 +429,7 @@ PrintObject::bridge_over_infill() #endif // compute the remaning internal solid surfaces as difference - ExPolygons not_to_bridge; - diff(internal_solid, to_bridge, ¬_to_bridge, true); + ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true); // build the new collection of fill_surfaces { diff --git a/xs/src/libslic3r/Surface.cpp b/xs/src/libslic3r/Surface.cpp index e4625f7998..e7d66c17b9 100644 --- a/xs/src/libslic3r/Surface.cpp +++ b/xs/src/libslic3r/Surface.cpp @@ -2,6 +2,11 @@ namespace Slic3r { +Surface::operator Polygons() const +{ + return this->expolygon; +} + double Surface::area() const { diff --git a/xs/src/libslic3r/Surface.hpp b/xs/src/libslic3r/Surface.hpp index 28a90799a2..a4a28a2c39 100644 --- a/xs/src/libslic3r/Surface.hpp +++ b/xs/src/libslic3r/Surface.hpp @@ -21,6 +21,7 @@ class Surface : surface_type(_surface_type), expolygon(_expolygon), thickness(-1), thickness_layers(1), bridge_angle(-1), extra_perimeters(0) {}; + operator Polygons() const; double area() const; bool is_solid() const; bool is_external() const; diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/xs/src/libslic3r/SurfaceCollection.cpp index 38c77ffd6e..2e96d4b1e1 100644 --- a/xs/src/libslic3r/SurfaceCollection.cpp +++ b/xs/src/libslic3r/SurfaceCollection.cpp @@ -111,6 +111,12 @@ SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons) } } +void +SurfaceCollection::append(const SurfaceCollection &coll) +{ + this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end()); +} + #ifdef SLIC3RXS REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); #endif diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index e2ced7f0e9..40e36c5f03 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -11,6 +11,9 @@ class SurfaceCollection public: Surfaces surfaces; + SurfaceCollection() {}; + SurfaceCollection(const Surfaces &_surfaces) + : surfaces(_surfaces) {}; operator Polygons() const; operator ExPolygons() const; void simplify(double tolerance); @@ -19,6 +22,7 @@ class SurfaceCollection template bool any_bottom_contains(const T &item) const; SurfacesPtr filter_by_type(SurfaceType type); void filter_by_type(SurfaceType type, Polygons* polygons); + void append(const SurfaceCollection &coll); }; } diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 87d98caf1f..67ff6979c7 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -16,6 +16,7 @@ #define LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER 0.15 #define SMALL_PERIMETER_LENGTH (6.5 / SCALING_FACTOR) * 2 * PI #define INSET_OVERLAP_TOLERANCE 0.4 +#define EXTERNAL_INFILL_MARGIN 3 #define scale_(val) (val / SCALING_FACTOR) #define unscale(val) (val * SCALING_FACTOR) #define SCALED_EPSILON scale_(EPSILON) diff --git a/xs/xsp/BridgeDetector.xsp b/xs/xsp/BridgeDetector.xsp index e2318bcbf9..0350dae871 100644 --- a/xs/xsp/BridgeDetector.xsp +++ b/xs/xsp/BridgeDetector.xsp @@ -9,14 +9,10 @@ ~BridgeDetector(); bool detect_angle(); - Polygons coverage() - %code{% THIS->coverage(&RETVAL); %}; - Polygons coverage_by_angle(double angle) - %code{% THIS->coverage(angle, &RETVAL); %}; - Polylines unsupported_edges() - %code{% THIS->unsupported_edges(&RETVAL); %}; - Polylines unsupported_edges_by_angle(double angle) - %code{% THIS->unsupported_edges(angle, &RETVAL); %}; + Polygons coverage(); + %name{coverage_by_angle} Polygons coverage(double angle); + Polylines unsupported_edges(); + %name{unsupported_edges_by_angle} Polylines unsupported_edges(double angle); double angle() %code{% RETVAL = THIS->angle; %}; double resolution() diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index 00913e615e..42338fb160 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -17,8 +17,8 @@ %code%{ RETVAL = &THIS->thin_fills; %}; Ref fill_surfaces() %code%{ RETVAL = &THIS->fill_surfaces; %}; - Ref bridged() - %code%{ RETVAL = &THIS->bridged; %}; + Polygons bridged() + %code%{ RETVAL = THIS->bridged; %}; Ref unsupported_bridge_edges() %code%{ RETVAL = &THIS->unsupported_bridge_edges; %}; Ref perimeters() @@ -32,6 +32,7 @@ void prepare_fill_surfaces(); void make_perimeters(SurfaceCollection* slices, SurfaceCollection* fill_surfaces) %code%{ THIS->make_perimeters(*slices, fill_surfaces); %}; + double infill_area_threshold(); }; %name{Slic3r::Layer} class Layer { diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 635b858311..d7631cdf96 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 process_external_surfaces(); void bridge_over_infill(); int ptr()