diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 5398be0a86..86f532589a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -27,6 +27,7 @@ has '_seam_position' => (is => 'ro', default => sub { {} }); # $object => p has 'first_layer' => (is => 'rw', default => sub {0}); # this flag triggers first layer speeds has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); +has 'volumetric_speed' => (is => 'rw', default => sub {0}); sub apply_print_config { my ($self, $print_config) = @_; @@ -298,11 +299,13 @@ sub _extrude_path { die "Invalid speed"; } } - my $F = $speed * 60; # convert mm/sec to mm/min - if ($self->first_layer) { - $F = $self->config->get_abs_value_over('first_layer_speed', $F/60) * 60; + $speed = $self->config->get_abs_value_over('first_layer_speed', $speed); } + if ($self->volumetric_speed != 0) { + $speed ||= $self->volumetric_speed / $path->mm3_per_mm; + } + my $F = $speed * 60; # convert mm/sec to mm/min # extrude arc or line $gcode .= ";_BRIDGE_FAN_START\n" if $path->is_bridge && $self->enable_cooling_markers; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 21e1babed0..5940942cdb 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -450,6 +450,7 @@ sub build { infill_every_layers infill_only_where_needed solid_infill_every_layers fill_angle solid_infill_below_area only_retract_when_crossing_perimeters infill_first + max_print_speed max_volumetric_speed perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed support_material_speed support_material_interface_speed bridge_speed gap_fill_speed @@ -607,6 +608,11 @@ sub build { $optgroup->append_single_option_line('first_layer_acceleration'); $optgroup->append_single_option_line('default_acceleration'); } + { + my $optgroup = $page->new_optgroup('Autospeed (advanced)'); + $optgroup->append_single_option_line('max_print_speed'); + $optgroup->append_single_option_line('max_volumetric_speed'); + } } { diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 7813ba1ad4..ea7dafcc13 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -15,7 +15,7 @@ has '_brim_done' => (is => 'rw'); has '_second_layer_things_done' => (is => 'rw'); has '_last_obj_copy' => (is => 'rw'); -use List::Util qw(first sum); +use List::Util qw(first sum min max); use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y scale unscale chained_path convex_hull); use Slic3r::Geometry::Clipper qw(JT_SQUARE union_ex offset); @@ -40,9 +40,60 @@ sub BUILD { layer_count => $layer_count, enable_cooling_markers => 1, ); + $self->_gcodegen($gcodegen); $gcodegen->apply_print_config($self->config); $gcodegen->set_extruders($self->print->extruders); - $self->_gcodegen($gcodegen); + + # initialize autospeed + { + # get the minimum cross-section used in the print + my @mm3_per_mm = (); + foreach my $object (@{$self->print->objects}) { + foreach my $region_id (0..$#{$self->print->regions}) { + my $region = $self->print->get_region($region_id); + foreach my $layer (@{$object->layers}) { + my $layerm = $layer->get_region($region_id); + if ($region->config->get_abs_value('perimeter_speed') == 0 + || $region->config->get_abs_value('small_perimeter_speed') == 0 + || $region->config->get_abs_value('external_perimeter_speed') == 0 + || $region->config->get_abs_value('bridge_speed') == 0) { + push @mm3_per_mm, $layerm->perimeters->min_mm3_per_mm; + } + if ($region->config->get_abs_value('infill_speed') == 0 + || $region->config->get_abs_value('solid_infill_speed') == 0 + || $region->config->get_abs_value('top_solid_infill_speed') == 0 + || $region->config->get_abs_value('bridge_speed') == 0) { + push @mm3_per_mm, $layerm->fills->min_mm3_per_mm; + } + } + } + if ($object->config->get_abs_value('support_material_speed') == 0 + || $object->config->get_abs_value('support_material_interface_speed') == 0) { + foreach my $layer (@{$object->support_layers}) { + push @mm3_per_mm, $layer->support_fills->min_mm3_per_mm; + push @mm3_per_mm, $layer->support_interface_fills->min_mm3_per_mm; + } + } + } + my $min_mm3_per_mm = min(@mm3_per_mm); + if ($min_mm3_per_mm > 0) { + # In order to honor max_print_speed we need to find a target volumetric + # speed that we can use throughout the print. So we define this target + # volumetric speed as the volumetric speed produced by printing the + # smallest cross-section at the maximum speed: any larger cross-section + # will need slower feedrates. + my $volumetric_speed = $min_mm3_per_mm * $self->config->max_print_speed; + + # limit such volumetric speed with max_volumetric_speed if set + if ($self->config->max_volumetric_speed > 0) { + $volumetric_speed = min( + $volumetric_speed, + $self->config->max_volumetric_speed, + ); + } + $gcodegen->volumetric_speed($volumetric_speed); + } + } } $self->_cooling_buffer(Slic3r::GCode::CoolingBuffer->new( diff --git a/utils/post-processing/flowrate.pl b/utils/post-processing/flowrate.pl index 573597c4cc..7aeef24dcf 100755 --- a/utils/post-processing/flowrate.pl +++ b/utils/post-processing/flowrate.pl @@ -6,17 +6,28 @@ use strict; use warnings; +use constant PI => 3.141592653589793238; +my @filament_diameter = split /,/, $ENV{SLIC3R_FILAMENT_DIAMETER}; + my $E = 0; -my ($X, $Y); +my $T = 0; +my ($X, $Y, $F); while (<>) { + if (/^G1.*? F([0-9.]+)/) { + $F = $1; + } if (/^G1 X([0-9.]+) Y([0-9.]+).*? E([0-9.]+)/) { my ($x, $y, $e) = ($1, $2, $3); my $e_length = $e - $E; if ($e_length > 0 && defined $X && defined $Y) { my $dist = sqrt( (($x-$X)**2) + (($y-$Y)**2) ); if ($dist > 0) { - my $flowrate = sprintf '%.2f', $e_length / $dist; - s/(\R+)/ ; XY dist = $dist ; E dist = $e_length ; E\/XY = $flowrate mm\/mm$1/; + my $mm_per_mm = $e_length / $dist; # dE/dXY + my $mm3_per_mm = ($filament_diameter[$T] ** 2) * PI/4 * $mm_per_mm; + my $vol_speed = $F/60 * $mm3_per_mm; + my $comment = sprintf ' ; dXY = %.3fmm ; dE = %.5fmm ; dE/XY = %.5fmm/mm; volspeed = %.5fmm^3/sec', + $dist, $e_length, $mm_per_mm, $vol_speed; + s/(\R+)/$comment$1/; } } $E = $e; @@ -33,6 +44,9 @@ if (/^G92 E0/) { $E = 0; } + if (/^T(\d+)/) { + $T = $1; + } print; } diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index f4ef37c13a..f6c3b80a8a 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -3,6 +3,7 @@ #include "ExPolygonCollection.hpp" #include "ClipperUtils.hpp" #include "Extruder.hpp" +#include #include namespace Slic3r { @@ -375,6 +376,20 @@ ExtrusionLoop::grow() const return pp; } +double +ExtrusionLoop::min_mm3_per_mm() const +{ + double min_mm3_per_mm = 0; + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { + if (min_mm3_per_mm == 0) { + min_mm3_per_mm = path->mm3_per_mm; + } else { + min_mm3_per_mm = fmin(min_mm3_per_mm, path->mm3_per_mm); + } + } + return min_mm3_per_mm; +} + #ifdef SLIC3RXS REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); #endif diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index b5262eae05..74541832f0 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -50,6 +50,7 @@ class ExtrusionEntity virtual Point first_point() const = 0; virtual Point last_point() const = 0; virtual Polygons grow() const = 0; + virtual double min_mm3_per_mm() const = 0; }; typedef std::vector ExtrusionEntitiesPtr; @@ -81,6 +82,9 @@ class ExtrusionPath : public ExtrusionEntity double xofs, double yofs, std::string extrusion_axis, std::string gcode_line_suffix) const; Polygons grow() const; + double min_mm3_per_mm() const { + return this->mm3_per_mm; + }; private: void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; @@ -117,6 +121,7 @@ class ExtrusionLoop : public ExtrusionEntity bool is_infill() const; bool is_solid_infill() const; Polygons grow() const; + double min_mm3_per_mm() const; }; } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index 075429d9de..79736cbcb7 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -1,5 +1,6 @@ #include "ExtrusionEntityCollection.hpp" #include +#include #include namespace Slic3r { @@ -137,6 +138,37 @@ ExtrusionEntityCollection::items_count() const return count; } +/* Returns a single vector of pointers to all non-collection items contained in this one */ +void +ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const +{ + for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { + if ((*it)->is_collection()) { + ExtrusionEntityCollection* collection = dynamic_cast(*it); + ExtrusionEntityCollection contents; + collection->flatten(&contents); + retval->entities.insert(retval->entities.end(), contents.entities.begin(), contents.entities.end()); + } else { + retval->entities.push_back((*it)->clone()); + } + } +} + +double +ExtrusionEntityCollection::min_mm3_per_mm() const +{ + double min_mm3_per_mm = 0; + for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { + double mm3_per_mm = (*it)->min_mm3_per_mm(); + if (min_mm3_per_mm == 0) { + min_mm3_per_mm = mm3_per_mm; + } else { + min_mm3_per_mm = fmin(min_mm3_per_mm, mm3_per_mm); + } + } + return min_mm3_per_mm; +} + #ifdef SLIC3RXS // there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 18e15e3188..24016d6383 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -30,6 +30,8 @@ class ExtrusionEntityCollection : public ExtrusionEntity Point last_point() const; Polygons grow() const; size_t items_count() const; + void flatten(ExtrusionEntityCollection* retval) const; + double min_mm3_per_mm() const; }; } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 8252fe2d2f..9c8c1738f0 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -61,7 +61,7 @@ class PrintRegion private: Print* _print; - + PrintRegion(Print* print); ~PrintRegion(); }; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 7d454eae8c..f14fac1a05 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -501,6 +501,22 @@ PrintConfigDef::build_def() { Options["min_print_speed"].min = 0; Options["min_print_speed"].max = 1000; + Options["max_print_speed"].type = coFloat; + Options["max_print_speed"].label = "Max print speed"; + Options["max_print_speed"].tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed in order to keep constant extruder pressure. This experimental setting is used to set the highest print speed you want to allow."; + Options["max_print_speed"].sidetext = "mm/s"; + Options["max_print_speed"].cli = "max-print-speed=f"; + Options["max_print_speed"].min = 1; + Options["max_print_speed"].max = 1000; + + Options["max_volumetric_speed"].type = coFloat; + Options["max_volumetric_speed"].label = "Max volumetric speed"; + Options["max_volumetric_speed"].tooltip = "When setting other speed settings to 0 Slic3r will autocalculate the optimal speed in order to keep constant extruder pressure. This experimental setting is used to set the maximum volumetric speed your extruder supports."; + Options["max_volumetric_speed"].sidetext = "mm³/s"; + Options["max_volumetric_speed"].cli = "max-volumetric-speed=f"; + Options["max_volumetric_speed"].min = 0; + Options["max_volumetric_speed"].max = 1000; + Options["min_skirt_length"].type = coFloat; Options["min_skirt_length"].label = "Minimum extrusion length"; Options["min_skirt_length"].tooltip = "Generate no less than the number of skirt loops required to consume the specified amount of filament on the bottom layer. For multi-extruder machines, this minimum applies to each extruder."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 287f33cf7c..4a3c4a2696 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -306,6 +306,8 @@ class GCodeConfig : public virtual StaticPrintConfig ConfigOptionBool gcode_comments; ConfigOptionEnum gcode_flavor; ConfigOptionString layer_gcode; + ConfigOptionFloat max_print_speed; + ConfigOptionFloat max_volumetric_speed; ConfigOptionFloat pressure_advance; ConfigOptionFloats retract_length; ConfigOptionFloats retract_length_toolchange; @@ -331,6 +333,8 @@ class GCodeConfig : public virtual StaticPrintConfig this->gcode_comments.value = false; this->gcode_flavor.value = gcfRepRap; this->layer_gcode.value = ""; + this->max_print_speed.value = 80; + this->max_volumetric_speed.value = 0; this->pressure_advance.value = 0; this->retract_length.values.resize(1); this->retract_length.values[0] = 2; @@ -361,6 +365,8 @@ class GCodeConfig : public virtual StaticPrintConfig if (opt_key == "gcode_comments") return &this->gcode_comments; if (opt_key == "gcode_flavor") return &this->gcode_flavor; if (opt_key == "layer_gcode") return &this->layer_gcode; + if (opt_key == "max_print_speed") return &this->max_print_speed; + if (opt_key == "max_volumetric_speed") return &this->max_volumetric_speed; if (opt_key == "pressure_advance") return &this->pressure_advance; if (opt_key == "retract_length") return &this->retract_length; if (opt_key == "retract_length_toolchange") return &this->retract_length_toolchange; diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index 08577c8926..b7e439479b 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -28,6 +28,12 @@ %code{% RETVAL = THIS->entities.size(); %}; int items_count() %code{% RETVAL = THIS->items_count(); %}; + ExtrusionEntityCollection* flatten() + %code{% + RETVAL = new ExtrusionEntityCollection(); + THIS->flatten(RETVAL); + %}; + double min_mm3_per_mm(); bool empty() %code{% RETVAL = THIS->entities.empty(); %}; std::vector orig_indices()