Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimistic print time estimator #3747

Closed
wants to merge 8 commits into from
66 changes: 65 additions & 1 deletion lib/Slic3r/GUI/Plater.pm
Expand Up @@ -142,6 +142,7 @@ sub new {
$self->stop_background_process;
$self->statusbar->SetStatusText("Slicing cancelled");
$self->{preview_notebook}->SetSelection(0);

});
$self->start_background_process;
} else {
Expand Down Expand Up @@ -431,6 +432,46 @@ sub new {
}
}
}

my $print_info_sizer;
{
my $box = Wx::StaticBox->new($self, -1, "Sliced Info");
$print_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
$print_info_sizer->SetMinSize([350,-1]);
my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5);
$grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
$grid_sizer->AddGrowableCol(1, 1);
$grid_sizer->AddGrowableCol(3, 1);
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
my @info = (
fil_cm => "Used Filament (cm)",
fil_cm3 => "Used Filament (cm^3)",
fil_g => "Used Filament (g)",
cost => "Cost",
time => "Time (dd:hh:mm:ss)",
);
my @tooltip = (
fil_cm => "Linear length of filament used (cm).",
fil_cm3 => "Volume of filament.",
fil_g => "Total weight of filament (in grams). This requires filament density to be set.",
cost => "Total cost in whatever currency you have set.",
time => "Optimistic time estimate for print completion. Only considers travel moves and printing moves.",
);
while (my $field = shift @info) {
my $label = shift @info;
my $tip = shift @tooltip;
my $text = Wx::StaticText->new($self, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
$text->SetFont($Slic3r::GUI::small_font);
$grid_sizer->Add($text, 0);

$self->{"print_info_$field"} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
$self->{"print_info_$field"}->SetToolTip($tip);
$self->{"print_info_$field"}->SetFont($Slic3r::GUI::small_font);
$grid_sizer->Add($self->{"print_info_$field"}, 0);
}
$self->{"sliced_info_box"} = $print_info_sizer;

}

my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
$buttons_sizer->AddStretchSpacer(1);
Expand All @@ -444,6 +485,9 @@ sub new {
$right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM, 5);
$right_sizer->Add($self->{list}, 1, wxEXPAND, 5);
$right_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
$right_sizer->Add($print_info_sizer, 0, wxEXPAND, 0);
$right_sizer->Hide($print_info_sizer);
$self->{"right_sizer"} = $right_sizer;

my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
$hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1);
Expand Down Expand Up @@ -1090,6 +1134,11 @@ sub async_apply_config {
if ($invalidated) {
# kill current thread if any
$self->stop_background_process;
# remove the sliced statistics box because something changed.
if ($self->{"right_sizer"}) {
$self->{"right_sizer"}->Hide($self->{"sliced_info_box"});
$self->{"right_sizer"}->Layout;
}
} else {
$self->resume_background_process;
}
Expand Down Expand Up @@ -1263,6 +1312,8 @@ sub export_gcode {

# this updates buttons status
$self->object_list_changed;
$self->{"right_sizer"}->Show($self->{"sliced_info_box"});
$self->{"right_sizer"}->Layout;

return $self->{export_gcode_output_file};
}
Expand Down Expand Up @@ -1357,6 +1408,12 @@ sub on_export_completed {
$self->send_gcode if $send_gcode;
$self->{print_file} = undef;
$self->{send_gcode_file} = undef;
$self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost));
$self->{"print_info_fil_g"}->SetLabel(sprintf("%.2f" , $self->{print}->total_weight));
$self->{"print_info_fil_cm3"}->SetLabel(sprintf("%.2f" , $self->{print}->total_extruded_volume) / 1000);
$self->{"print_info_fil_cm"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament) / 10);
$self->{"print_info_time"}->SetLabel(sprintf "%02d:%02d:%02d:%02d", (gmtime($self->{print}->total_time))[7,2,1,0]);


# this updates buttons status
$self->object_list_changed;
Expand Down Expand Up @@ -1553,7 +1610,10 @@ sub update {
} else {
$self->resume_background_process;
}

if ($self->{"right_sizer"}) {
$self->{"right_sizer"}->Hide($self->{"sliced_info_box"});
$self->{"right_sizer"}->Layout;
}
$self->refresh_canvases;
}

Expand Down Expand Up @@ -1627,6 +1687,10 @@ sub on_config_change {
$self->Layout;
}
}
if ($self->{"right_sizer"}) {
$self->{"right_sizer"}->Hide($self->{"sliced_info_box"});
$self->{"right_sizer"}->Layout;
}

return if !$self->GetFrame->is_loaded;

Expand Down
4 changes: 3 additions & 1 deletion lib/Slic3r/GUI/Tab.pm
Expand Up @@ -897,7 +897,7 @@ sub build {
my $self = shift;

$self->init_config_options(qw(
filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier
filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier filament_density filament_cost
temperature first_layer_temperature bed_temperature first_layer_bed_temperature
fan_always_on cooling
min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers
Expand All @@ -912,6 +912,8 @@ sub build {
$optgroup->append_single_option_line('filament_colour', 0);
$optgroup->append_single_option_line('filament_diameter', 0);
$optgroup->append_single_option_line('extrusion_multiplier', 0);
$optgroup->append_single_option_line('filament_density', 0);
$optgroup->append_single_option_line('filament_cost', 0);
}

{
Expand Down
21 changes: 21 additions & 0 deletions lib/Slic3r/Print/GCode.pm
Expand Up @@ -255,17 +255,38 @@ sub export {
$self->print->clear_filament_stats;
$self->print->total_used_filament(0);
$self->print->total_extruded_volume(0);
$self->print->total_weight(0);
$self->print->total_cost(0);
$self->print->total_time(0);
foreach my $extruder (@{$gcodegen->writer->extruders}) {
my $used_filament = $extruder->used_filament;
my $extruded_volume = $extruder->extruded_volume;
my $filament_weight = $extruded_volume * $extruder->filament_density / 1000;
my $filament_cost = $filament_weight * ($extruder->filament_cost / 1000);
my $extrusion_time = $extruder->extrusion_time;
$self->print->set_filament_stats($extruder->id, $used_filament);

printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
$used_filament, $extruded_volume/1000;
if ($filament_weight > 0) {
$self->print->total_weight($self->print->total_weight + $filament_weight);
printf $fh "; filament used = %.1fg\n",
$filament_weight;
if ($filament_cost > 0) {
$self->print->total_cost($self->print->total_cost + $filament_cost);
printf $fh "; filament cost = %.1f\n",
$filament_cost;
}
}
printf $fh "; estimated print time (optimistic) = %02d:%02d:%02d:%02d (days:h:m:s)\n",
(gmtime($extrusion_time))[7,2,1,0];

$self->print->total_used_filament($self->print->total_used_filament + $used_filament);
$self->print->total_time($self->print->total_time + $extrusion_time);
$self->print->total_extruded_volume($self->print->total_extruded_volume + $extruded_volume);
}
printf $fh "; total filament cost = %.1f\n",
$self->print->total_cost;

# append full config
print $fh "\n";
Expand Down
2 changes: 2 additions & 0 deletions lib/Slic3r/Print/Object.pm
Expand Up @@ -417,6 +417,8 @@ sub generate_support_material {
$self->_support_material->generate($self);

$self->set_step_done(STEP_SUPPORTMATERIAL);
my $stats = "Weight: %.1fg, Cost: %.1f" , $self->print->total_weight, $self->print->total_cost;
$self->print->status_cb->(85, $stats);
}

sub _support_material {
Expand Down
52 changes: 51 additions & 1 deletion xs/src/libslic3r/Extruder.cpp
Expand Up @@ -4,7 +4,8 @@ namespace Slic3r {

Extruder::Extruder(unsigned int id, GCodeConfig *config)
: id(id),
config(config)
config(config),
time_used(0.0)
{
reset();

Expand Down Expand Up @@ -111,6 +112,18 @@ Extruder::filament_diameter() const
return this->config->filament_diameter.get_at(this->id);
}

double
Extruder::filament_density() const
{
return this->config->filament_density.get_at(this->id);
}

double
Extruder::filament_cost() const
{
return this->config->filament_cost.get_at(this->id);
}

double
Extruder::extrusion_multiplier() const
{
Expand Down Expand Up @@ -153,4 +166,41 @@ Extruder::retract_restart_extra_toolchange() const
return this->config->retract_restart_extra_toolchange.get_at(this->id);
}

// Wildly optimistic acceleration "bell" curve modeling.
// Adds an estimate of how long the move with a given accel
// takes, in seconds, to the extruder's total time count.
// It is assumed that the movement is smooth and uniform.
void
Extruder::add_extrusion_time(double length, double v, double acceleration)
{
// for half of the move, there are 2 zones, where the speed is increasing/decreasing and
// where the speed is constant.
// Since the slowdown is assumed to be uniform, calculate the average velocity for half of the
// expected displacement.
// final velocity v = a*t => a * (dx / 0.5v) => v^2 = 2*a*dx
// v_avg = 0.5v => 2*v_avg = v
// d_x = v_avg*t => t = d_x / v_avg
acceleration = (acceleration == 0.0 ? 4000.0 : acceleration); // Set a default accel to use for print time in case it's 0 somehow.
auto half_length = length / 2.0;
auto t_init = v / acceleration; // time to final velocity
auto dx_init = (0.5*v*t_init); // Initial displacement for the time to get to final velocity
auto t = 0.0;
if (half_length >= dx_init) {
half_length -= (0.5*v*t_init);
t += t_init;
t += (half_length / v); // rest of time is at constant speed.
} else {
// If too much displacement for the expected final velocity, we don't hit the max, so reduce
// the average velocity to fit the displacement we actually are looking for.
t += std::sqrt(std::abs(length) * 2.0 * acceleration) / acceleration;
}
this->time_used += 2.0*t; // cut in half before, so double to get full time spent.
}

double
Extruder::extrusion_time() const
{
return this->time_used;
}

}
5 changes: 5 additions & 0 deletions xs/src/libslic3r/Extruder.hpp
Expand Up @@ -17,6 +17,7 @@ class Extruder
double restart_extra;
double e_per_mm3;
double retract_speed_mm_min;
double time_used;

Extruder(unsigned int id, GCodeConfig *config);
virtual ~Extruder() {}
Expand All @@ -29,13 +30,17 @@ class Extruder
double used_filament() const;

double filament_diameter() const;
double filament_density() const;
double filament_cost() const;
double extrusion_multiplier() const;
double retract_length() const;
double retract_lift() const;
int retract_speed() const;
double retract_restart_extra() const;
double retract_length_toolchange() const;
double retract_restart_extra_toolchange() const;
void add_extrusion_time(double length, double v, double acceleration);
double extrusion_time() const;

private:
GCodeConfig *config;
Expand Down
9 changes: 7 additions & 2 deletions xs/src/libslic3r/GCode.cpp
Expand Up @@ -186,6 +186,7 @@ Wipe::wipe(GCode &gcodegen, bool toolchange)
-dE,
"wipe and retract"
);
gcodegen.writer.extruder()->add_extrusion_time(dE, wipe_speed, 9000.0);
retracted += dE;
}
gcodegen.writer.extruder()->retracted += retracted;
Expand Down Expand Up @@ -510,9 +511,9 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed)
// compensate retraction
gcode += this->unretract();

double acceleration = 1.0;
// adjust acceleration
{
double acceleration;
if (this->config.first_layer_acceleration.value > 0 && this->first_layer) {
acceleration = this->config.first_layer_acceleration.value;
} else if (this->config.perimeter_acceleration.value > 0 && path.is_perimeter()) {
Expand Down Expand Up @@ -592,6 +593,7 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed)
e_per_mm * line_length,
comment
);
this->writer.extruder()->add_extrusion_time(line_length, speed, acceleration);
}
}
if (this->wipe.enable) {
Expand Down Expand Up @@ -649,8 +651,11 @@ GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)

// use G1 because we rely on paths being straight (G0 may make round paths)
Lines lines = travel.lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
// wildly optimistic, assume travel accel is very fast.
this->writer.extruder()->add_extrusion_time(line->length() * SCALING_FACTOR, this->config.travel_speed, 9000.0);
gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment);
}

/* While this makes the estimate more accurate, CoolingBuffer calculates the slowdown
factor on the whole elapsed time but only alters non-travel moves, thus the resulting
Expand Down
3 changes: 2 additions & 1 deletion xs/src/libslic3r/Print.cpp
Expand Up @@ -54,7 +54,8 @@ template class PrintState<PrintObjectStep>;

Print::Print()
: total_used_filament(0),
total_extruded_volume(0)
total_extruded_volume(0),
total_time(0)
{
}

Expand Down
2 changes: 1 addition & 1 deletion xs/src/libslic3r/Print.hpp
Expand Up @@ -167,7 +167,7 @@ class Print
PrintRegionPtrs regions;
PlaceholderParser placeholder_parser;
// TODO: status_cb
double total_used_filament, total_extruded_volume;
double total_used_filament, total_extruded_volume, total_cost, total_weight, total_time;
std::map<size_t,float> filament_stats;
PrintState<PrintStep> state;

Expand Down
24 changes: 24 additions & 0 deletions xs/src/libslic3r/PrintConfig.cpp
Expand Up @@ -355,6 +355,30 @@ PrintConfigDef::PrintConfigDef()
opt->values.push_back(3);
def->default_value = opt;
}

def = this->add("filament_density", coFloats);
def->label = "Density";
def->tooltip = "Enter your filament density here. This is only for statistical information. A decent way is to weigh a known length of filament and compute the ratio of the length to volume. Better is to calculate the volume directly through displacement.";
def->sidetext = "g/cm^3";
def->cli = "filament-density=f@";
def->min = 0;
{
ConfigOptionFloats* opt = new ConfigOptionFloats();
opt->values.push_back(0);
def->default_value = opt;
}

def = this->add("filament_cost", coFloats);
def->label = "Cost";
def->tooltip = "Enter your filament cost per kg here. This is only for statistical information.";
def->sidetext = "money/kg";
def->cli = "filament-cost=f@";
def->min = 0;
{
ConfigOptionFloats* opt = new ConfigOptionFloats();
opt->values.push_back(0);
def->default_value = opt;
}

def = this->add("filament_settings_id", coString);
def->default_value = new ConfigOptionString("");
Expand Down
4 changes: 4 additions & 0 deletions xs/src/libslic3r/PrintConfig.hpp
Expand Up @@ -289,6 +289,8 @@ class GCodeConfig : public virtual StaticPrintConfig
ConfigOptionString extrusion_axis;
ConfigOptionFloats extrusion_multiplier;
ConfigOptionFloats filament_diameter;
ConfigOptionFloats filament_density;
ConfigOptionFloats filament_cost;
ConfigOptionFloats filament_max_volumetric_speed;
ConfigOptionBool gcode_comments;
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
Expand Down Expand Up @@ -322,6 +324,8 @@ class GCodeConfig : public virtual StaticPrintConfig
OPT_PTR(extrusion_axis);
OPT_PTR(extrusion_multiplier);
OPT_PTR(filament_diameter);
OPT_PTR(filament_density);
OPT_PTR(filament_cost);
OPT_PTR(filament_max_volumetric_speed);
OPT_PTR(gcode_comments);
OPT_PTR(gcode_flavor);
Expand Down