diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 87d0398ca..e18fa6abf 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -209,112 +209,9 @@ sub make_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, 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->_make_skirt(); $self->set_step_done(STEP_SKIRT); } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 1619f8c7b..44a5abd1f 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 "Extruder.hpp" #include "Flow.hpp" #include "Geometry.hpp" #include "SupportMaterial.hpp" @@ -839,9 +840,137 @@ Print::auto_assign_extruders(ModelObject* model_object) const //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. size_t extruder_id = (v - model_object->volumes.begin()) + 1; if (!(*v)->config.has("extruder")) - (*v)->config.opt("extruder", true)->value = extruder_id; + (*v)->config.opt("extruder", true)->value = int(extruder_id); } } } + +void Print::_make_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. + coordf_t skirt_height_z = 0.; + for (const PrintObject *object : this->objects) { + size_t skirt_layers = this->has_infinite_skirt() ? + object->layer_count() : + std::min(size_t(this->config.skirt_height.value), object->layer_count()); + skirt_height_z = std::max(skirt_height_z, object->layers[skirt_layers-1]->print_z); + } + + // Collect points from all layers contained in skirt height. + Points points; + for (const PrintObject *object : this->objects) { + Points object_points; + // Get object layers up to skirt_height_z. + for (const Layer *layer : object->layers) { + if (layer->print_z > skirt_height_z) + break; + for (const ExPolygon &expoly : layer->slices.expolygons) + // Collect the outer contour points only, ignore holes for the calculation of the convex hull. + append(object_points, expoly.contour.points); + } + // Get support layers up to skirt_height_z. + for (const SupportLayer *layer : object->support_layers) { + if (layer->print_z > skirt_height_z) + break; + for (const ExtrusionEntity *extrusion_entity : layer->support_fills.entities) + append(object_points, extrusion_entity->as_polyline().points); + for (const ExtrusionEntity *extrusion_entity : layer->support_interface_fills.entities) + append(object_points, extrusion_entity->as_polyline().points); + } + // Repeat points for each object copy. + for (const Point &shift : object->_shifted_copies) { + Points copy_points = object_points; + for (Point &pt : copy_points) + pt.translate(shift); + append(points, copy_points); + } + } + + if (points.size() < 3) + // At least three points required for a convex hull. + return; + + Polygon convex_hull = Slic3r::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 + double first_layer_height = this->skirt_first_layer_height(); + Flow flow = this->skirt_flow(); + float spacing = flow.spacing(); + double mm3_per_mm = flow.mm3_per_mm(); + + std::vector extruders; + std::vector extruders_e_per_mm; + { + auto set_extruders = this->extruders(); + extruders.reserve(set_extruders.size()); + extruders_e_per_mm.reserve(set_extruders.size()); + for (auto &extruder_id : set_extruders) { + extruders.push_back(extruder_id); + GCodeConfig config; + config.apply(this->config, true); + extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &config).e_per_mm(mm3_per_mm)); + } + } + + // Number of skirt loops per skirt layer. + int n_skirts = this->config.skirts.value; + if (this->has_infinite_skirt() && n_skirts == 0) + n_skirts = 1; + + // Initial offset of the brim inner edge from the object (possible with a support & raft). + // The skirt will touch the brim if the brim is extruded. + coord_t distance = scale_(std::max(this->config.skirt_distance.value, this->config.brim_width.value)); + // 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. + std::vector extruded_length(extruders.size(), 0.); + for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) { + // Offset the skirt outside. + distance += coord_t(scale_(spacing)); + // Generate the skirt centerline. + Polygon loop = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1)).front(); + // Extrude the skirt loop. + ExtrusionLoop eloop(elrSkirt); + eloop.paths.emplace_back(ExtrusionPath( + 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 + ))); + eloop.paths.back().polyline = loop.split_at_first_point(); + this->skirt.append(eloop); + if (this->config.min_skirt_length.value > 0) { + // The skirt length is limited. Sum the total amount of filament length extruded, in mm. + extruded_length[extruder_idx] += unscale(loop.length()) * extruders_e_per_mm[extruder_idx]; + if (extruded_length[extruder_idx] < this->config.min_skirt_length.value) { + // Not extruded enough yet with the current extruder. Add another loop. + if (i == 1) + ++ i; + } else { + assert(extruded_length[extruder_idx] >= this->config.min_skirt_length.value); + // Enough extruded with the current extruder. Extrude with the next one, + // until the prescribed number of skirt loops is extruded. + if (extruder_idx + 1 < extruders.size()) + ++ extruder_idx; + } + } else { + // The skirt lenght is not limited, extrude the skirt with the 1st extruder only. + } + } + // Brims were generated inside out, reverse to print the outmost contour first. + this->skirt.reverse(); +} + } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 30d49b236..f18432da6 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -251,6 +251,8 @@ class Print double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object) const; + + void _make_skirt(); private: void clear_regions(); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index b70ff9c70..8651469cd 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -270,6 +270,8 @@ _constant() double skirt_first_layer_height(); Clone brim_flow(); Clone skirt_flow(); + + void _make_skirt(); %{ double