diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f3ad134e9..2bb298c19 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -350,126 +350,8 @@ sub make_brim { $_->generate_support_material for @{$self->objects}; $self->make_skirt; - return if $self->step_done(STEP_BRIM); - $self->set_step_started(STEP_BRIM); - - # since this method must be idempotent, we clear brim paths *before* - # checking whether we need to generate them - $self->brim->clear; - - if ($self->config->brim_width == 0 && $self->config->brim_connections_width == 0) { - $self->set_step_done(STEP_BRIM); - return; - } $self->status_cb->(88, "Generating brim"); - - # brim is only printed on first layer and uses perimeter extruder - my $first_layer_height = $self->skirt_first_layer_height; - my $flow = $self->brim_flow; - my $mm3_per_mm = $flow->mm3_per_mm; - - my $grow_distance = $flow->scaled_width / 2; - my @islands = (); # array of polygons - foreach my $obj_idx (0 .. ($self->object_count - 1)) { - my $object = $self->objects->[$obj_idx]; - my $layer0 = $object->get_layer(0); - my @object_islands = ( - (map $_->contour, @{$layer0->slices}), - ); - if (@{ $object->support_layers }) { - my $support_layer0 = $object->support_layers->[0]; - push @object_islands, - (map @{$_->polyline->grow($grow_distance)}, @{$support_layer0->support_fills}) - if $support_layer0->support_fills; - push @object_islands, - (map @{$_->polyline->grow($grow_distance)}, @{$support_layer0->support_interface_fills}) - if $support_layer0->support_interface_fills; - } - foreach my $copy (@{$object->_shifted_copies}) { - push @islands, map { $_->translate(@$copy); $_ } map $_->clone, @object_islands; - } - } - - my @loops = (); - my $num_loops = int($self->config->brim_width / $flow->width + 0.5); - for my $i (reverse 1 .. $num_loops) { - # JT_SQUARE ensures no vertex is outside the given offset distance - # -0.5 because islands are not represented by their centerlines - # (first offset more, then step back - reverse order than the one used for - # perimeters because here we're offsetting outwards) - push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)}; - } - - $self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths( - Slic3r::ExtrusionPath->new( - polyline => Slic3r::Polygon->new(@$_)->split_at_first_point, - role => EXTR_ROLE_SKIRT, - mm3_per_mm => $mm3_per_mm, - width => $flow->width, - height => $first_layer_height, - ), - ), reverse @{union_pt_chained(\@loops)}); - - if ($self->config->brim_connections_width > 0) { - # get islands to connects - @islands = map convex_hull(\@$_), @islands; - @islands = @{offset(\@islands, ($num_loops-0.2) * $flow->scaled_spacing, 10000, JT_SQUARE)}; - - # compute centroid for each island - my @centroids = map $_->centroid, @islands; - - # in order to check visibility we need to account for the connections width, - # so let's use grown islands - my $scaled_width = scale($self->config->brim_connections_width); - my $grown = offset(\@islands, +$scaled_width/2); - - # find pairs of islands having direct visibility - my @lines = (); - for my $i (0..$#islands) { - for my $j (($i+1)..$#islands) { - # check visibility - my $line = Slic3r::Line->new(@centroids[$i,$j]); - next if @{diff_pl([$line->as_polyline], $grown)} != 1; - - push @lines, $line; - } - } - - my $filler = Slic3r::Filler->new_from_type('rectilinear'); - $filler->set_spacing($flow->spacing); - $filler->set_dont_adjust(1); - - # subtract already generated connections in order to prevent crossings - # and overextrusion - my @other = (); - - foreach my $line (@lines) { - my $expolygons = diff_ex( - $line->grow($scaled_width/2), - [ @islands, @other ], - ); - push @other, map $_->clone, map @$_, @$expolygons; - - $filler->set_angle($line->direction); - foreach my $expolygon (@$expolygons) { - my $paths = $filler->fill_surface( - Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_BOTTOM), - layer_height => $first_layer_height, - density => 1, - ); - - $self->brim->append(map Slic3r::ExtrusionPath->new( - polyline => $_, - role => EXTR_ROLE_SKIRT, - mm3_per_mm => $mm3_per_mm, - width => $flow->width, - height => $first_layer_height, - ), @$paths); - } - } - } - - $self->set_step_done(STEP_BRIM); + $self->_make_brim; } # this method will return the supplied input file path after expanding its diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 7f411f1f4..1d7ef9a99 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -165,6 +165,9 @@ class ExtrusionLoop : public ExtrusionEntity // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const; Polyline as_polyline() const { return this->polygon().split_at_first_point(); } + void append(const ExtrusionPath &path) { + this->paths.push_back(path); + }; }; } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index ec3e28e6c..091cd3ff0 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -33,9 +33,16 @@ ExtrusionEntityCollection::swap (ExtrusionEntityCollection &c) } ExtrusionEntityCollection::~ExtrusionEntityCollection() +{ + this->clear(); +} + +void +ExtrusionEntityCollection::clear() { for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) delete *it; + this->entities.clear(); } ExtrusionEntityCollection::operator ExtrusionPaths() const diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 491411aa7..c3a26df82 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -30,9 +30,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity bool empty() const { return this->entities.empty(); }; - void clear() { - this->entities.clear(); - }; + void clear(); void swap (ExtrusionEntityCollection &c); void append(const ExtrusionEntity &entity); void append(const ExtrusionEntitiesPtr &entities); diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 8b918eebb..6fc7e4569 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1,6 +1,7 @@ #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" +#include "Fill/Fill.hpp" #include "Flow.hpp" #include "Geometry.hpp" #include "SupportMaterial.hpp" @@ -787,6 +788,141 @@ Print::skirt_flow() const ); } +void +Print::_make_brim() +{ + if (this->state.is_done(psBrim)) return; + this->state.set_started(psBrim); + + // since this method must be idempotent, we clear brim paths *before* + // checking whether we need to generate them + this->brim.clear(); + + if (this->config.brim_width == 0 && this->config.brim_connections_width == 0) { + this->state.set_done(psBrim); + return; + } + + // brim is only printed on first layer and uses perimeter extruder + const double first_layer_height = this->skirt_first_layer_height(); + const Flow flow = this->brim_flow(); + const double mm3_per_mm = flow.mm3_per_mm(); + + const coord_t grow_distance = flow.scaled_width()/2; + Polygons islands; + + FOREACH_OBJECT(this, object) { + const Layer &layer0 = *(*object)->get_layer(0); + + Polygons object_islands = layer0.slices.contours(); + + if (!(*object)->support_layers.empty()) { + const SupportLayer &support_layer0 = *(*object)->get_support_layer(0); + + for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_fills.entities.begin(); + it != support_layer0.support_fills.entities.end(); ++it) + append_to(object_islands, offset((*it)->as_polyline(), grow_distance)); + + for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_interface_fills.entities.begin(); + it != support_layer0.support_interface_fills.entities.end(); ++it) + append_to(object_islands, offset((*it)->as_polyline(), grow_distance)); + } + for (Points::const_iterator copy = (*object)->_shifted_copies.begin(); copy != (*object)->_shifted_copies.end(); + ++copy) { + for (Polygons::const_iterator p = object_islands.begin(); p != object_islands.end(); ++p) { + Polygon p2 = *p; + p2.translate(*copy); + islands.push_back(p2); + } + } + } + + Polygons loops; + const int num_loops = floor(this->config.brim_width / flow.width + 0.5); + for (int i = num_loops; i >= 1; --i) { + // JT_SQUARE ensures no vertex is outside the given offset distance + // -0.5 because islands are not represented by their centerlines + // (first offset more, then step back - reverse order than the one used for + // perimeters because here we're offsetting outwards) + append_to(loops, offset2( + islands, + flow.scaled_spacing() * (i + 0.5), + flow.scaled_spacing() * -1.0, + 100000, + ClipperLib::jtSquare + )); + } + + { + Polygons chained = union_pt_chained(loops); + for (Polygons::const_reverse_iterator p = chained.rbegin(); p != chained.rend(); ++p) { + ExtrusionPath path(erSkirt, mm3_per_mm, flow.width, first_layer_height); + path.polyline = p->split_at_first_point(); + this->brim.append(ExtrusionLoop(path)); + } + } + + if (this->config.brim_connections_width > 0) { + // get islands to connects + for (Polygons::iterator p = islands.begin(); p != islands.end(); ++p) + *p = Geometry::convex_hull(p->points); + + islands = offset(islands, flow.scaled_spacing() * (num_loops-0.2), 10000, jtSquare); + + // compute centroid for each island + Points centroids; + centroids.reserve(islands.size()); + for (Polygons::const_iterator p = islands.begin(); p != islands.end(); ++p) + centroids.push_back(p->centroid()); + + // in order to check visibility we need to account for the connections width, + // so let's use grown islands + const double scaled_width = scale_(this->config.brim_connections_width); + const Polygons grown = offset(islands, +scaled_width/2); + + // find pairs of islands having direct visibility + Lines lines; + for (size_t i = 0; i < islands.size(); ++i) { + for (size_t j = (i+1); j < islands.size(); ++j) { + // check visibility + Line line(centroids[i], centroids[j]); + if (diff_pl((Polyline)line, grown).size() != 1) continue; + lines.push_back(line); + } + } + + std::auto_ptr filler(Fill::new_from_type(ipRectilinear)); + filler->spacing = flow.spacing(); + filler->dont_adjust = true; + filler->density = 1; + + // subtract already generated connections in order to prevent crossings + // and overextrusion + Polygons other; + + for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { + ExPolygons expp = diff_ex( + offset((Polyline)*line, scaled_width/2), + islands + other + ); + + filler->angle = line->direction(); + for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { + append_to(other, (Polygons)*ex); + + const Polylines paths = filler->fill_surface(Surface(stBottom, *ex)); + for (Polylines::const_iterator pl = paths.begin(); pl != paths.end(); ++pl) { + ExtrusionPath path(erSkirt, mm3_per_mm, flow.width, first_layer_height); + path.polyline = *pl; + this->brim.append(path); + } + } + } + } + + this->state.set_done(psBrim); +} + PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &volume) diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 50b68d2a5..6cd24ef79 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -203,6 +203,7 @@ class Print double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; + void _make_brim(); std::set object_extruders() const; std::set support_material_extruders() const; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index b37890380..3992a0aff 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -228,6 +228,7 @@ _constant() double skirt_first_layer_height(); Clone brim_flow(); Clone skirt_flow(); + void _make_brim(); %{ double