diff options
-rw-r--r-- | lib/Slic3r/GUI/Plater.pm | 12 | ||||
-rw-r--r-- | t/perimeters.t | 8 | ||||
-rw-r--r-- | xs/src/libslic3r/GCode.cpp | 71 | ||||
-rw-r--r-- | xs/src/libslic3r/GCode.hpp | 9 | ||||
-rw-r--r-- | xs/src/libslic3r/GCodeTimeEstimator.cpp | 393 | ||||
-rw-r--r-- | xs/src/libslic3r/GCodeTimeEstimator.hpp | 69 | ||||
-rw-r--r-- | xs/src/libslic3r/Print.hpp | 3 | ||||
-rw-r--r-- | xs/src/libslic3r/PrintConfig.cpp | 101 | ||||
-rw-r--r-- | xs/src/libslic3r/PrintConfig.hpp | 5 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/3DScene.cpp | 2 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/Field.cpp | 52 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/Field.hpp | 29 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/GUI.cpp | 31 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/GUI.hpp | 7 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/OptionsGroup.cpp | 10 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/OptionsGroup.hpp | 12 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/Preset.cpp | 5 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/Tab.cpp | 118 | ||||
-rw-r--r-- | xs/src/slic3r/GUI/Tab.hpp | 4 | ||||
-rw-r--r-- | xs/xsp/GUI.xsp | 3 | ||||
-rw-r--r-- | xs/xsp/Print.xsp | 6 |
21 files changed, 773 insertions, 177 deletions
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index aef452f5e..4d421dc4a 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -535,7 +535,11 @@ sub new { fil_mm3 => L("Used Filament (mm³)"), fil_g => L("Used Filament (g)"), cost => L("Cost"), - time => L("Estimated printing time"), +#========================================================================================================================================== + normal_time => L("Estimated printing time (normal mode)"), +# default_time => L("Estimated printing time (default mode)"), +#========================================================================================================================================== + silent_time => L("Estimated printing time (silent mode)"), ); while (my $field = shift @info) { my $label = shift @info; @@ -1617,7 +1621,11 @@ sub on_export_completed { $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_mm3"}->SetLabel(sprintf("%.2f" , $self->{print}->total_extruded_volume)); - $self->{"print_info_time"}->SetLabel($self->{print}->estimated_print_time); +#========================================================================================================================================== + $self->{"print_info_normal_time"}->SetLabel($self->{print}->estimated_normal_print_time); +# $self->{"print_info_default_time"}->SetLabel($self->{print}->estimated_default_print_time); +#========================================================================================================================================== + $self->{"print_info_silent_time"}->SetLabel($self->{print}->estimated_silent_print_time); $self->{"print_info_fil_m"}->SetLabel(sprintf("%.2f" , $self->{print}->total_used_filament / 1000)); $self->{"print_info_box_show"}->(1); diff --git a/t/perimeters.t b/t/perimeters.t index ee332616d..d0657cb23 100644 --- a/t/perimeters.t +++ b/t/perimeters.t @@ -175,7 +175,7 @@ use Slic3r::Test; if ($info->{extruding} && $info->{dist_XY} > 0) { $cur_loop ||= [ [$self->X, $self->Y] ]; push @$cur_loop, [ @$info{qw(new_X new_Y)} ]; - } else { + } elsif ($cmd ne 'M73') { # skips remaining time lines (M73) if ($cur_loop) { $has_cw_loops = 1 if Slic3r::Polygon->new(@$cur_loop)->is_clockwise; $cur_loop = undef; @@ -201,7 +201,7 @@ use Slic3r::Test; if ($info->{extruding} && $info->{dist_XY} > 0) { $cur_loop ||= [ [$self->X, $self->Y] ]; push @$cur_loop, [ @$info{qw(new_X new_Y)} ]; - } else { + } elsif ($cmd ne 'M73') { # skips remaining time lines (M73) if ($cur_loop) { $has_cw_loops = 1 if Slic3r::Polygon->new_scale(@$cur_loop)->is_clockwise; if ($self->F == $config->external_perimeter_speed*60) { @@ -306,7 +306,7 @@ use Slic3r::Test; if ($info->{extruding} && $info->{dist_XY} > 0 && ($args->{F} // $self->F) == $config->perimeter_speed*60) { $perimeters{$self->Z}++ if !$in_loop; $in_loop = 1; - } else { + } elsif ($cmd ne 'M73') { # skips remaining time lines (M73) $in_loop = 0; } }); @@ -430,7 +430,7 @@ use Slic3r::Test; push @seam_points, Slic3r::Point->new_scale($self->X, $self->Y); } $was_extruding = 1; - } else { + } elsif ($cmd ne 'M73') { # skips remaining time lines (M73) $was_extruding = 0; } }); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 7ed78c433..e587c8e93 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -374,6 +374,12 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); } fclose(file); + + m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + + if (m_silent_time_estimator_enabled) + m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + if (! this->m_placeholder_parser_failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; @@ -403,10 +409,48 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) { PROFILE_FUNC(); - // resets time estimator - m_time_estimator.reset(); - m_time_estimator.set_dialect(print.config.gcode_flavor); - + // resets time estimators + m_normal_time_estimator.reset(); + m_normal_time_estimator.set_dialect(print.config.gcode_flavor); + m_normal_time_estimator.set_acceleration(print.config.machine_max_acceleration_extruding.values[0]); + m_normal_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[0]); + m_normal_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[0]); + m_normal_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[0]); + m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[0]); + m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[0]); + m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[0]); + + m_silent_time_estimator_enabled = (print.config.gcode_flavor == gcfMarlin) && print.config.silent_mode && boost::starts_with(print.config.printer_model.value, "MK3"); + if (m_silent_time_estimator_enabled) + { + m_silent_time_estimator.reset(); + m_silent_time_estimator.set_dialect(print.config.gcode_flavor); + m_silent_time_estimator.set_acceleration(print.config.machine_max_acceleration_extruding.values[1]); + m_silent_time_estimator.set_retract_acceleration(print.config.machine_max_acceleration_retracting.values[1]); + m_silent_time_estimator.set_minimum_feedrate(print.config.machine_min_extruding_rate.values[1]); + m_silent_time_estimator.set_minimum_travel_feedrate(print.config.machine_min_travel_rate.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config.machine_max_acceleration_x.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config.machine_max_acceleration_y.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config.machine_max_acceleration_z.values[1]); + m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config.machine_max_acceleration_e.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config.machine_max_feedrate_x.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config.machine_max_feedrate_y.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config.machine_max_feedrate_z.values[1]); + m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config.machine_max_feedrate_e.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config.machine_max_jerk_x.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config.machine_max_jerk_y.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config.machine_max_jerk_z.values[1]); + m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config.machine_max_jerk_e.values[1]); + } // resets analyzer m_analyzer.reset(); m_enable_analyzer = preview_data != nullptr; @@ -806,7 +850,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write(file, m_writer.postamble()); // calculates estimated printing time - m_time_estimator.calculate_time(); + m_normal_time_estimator.calculate_time(); + if (m_silent_time_estimator_enabled) + m_silent_time_estimator.calculate_time(); // Get filament stats. print.filament_stats.clear(); @@ -814,13 +860,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) print.total_extruded_volume = 0.; print.total_weight = 0.; print.total_cost = 0.; - print.estimated_print_time = m_time_estimator.get_time_hms(); + print.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); + print.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; for (const Extruder &extruder : m_writer.extruders()) { double used_filament = extruder.used_filament(); double extruded_volume = extruder.extruded_volume(); double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - print.filament_stats.insert(std::pair<size_t,float>(extruder.id(), used_filament)); + print.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament)); _write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001); if (filament_weight > 0.) { print.total_weight = print.total_weight + filament_weight; @@ -834,7 +881,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) print.total_extruded_volume = print.total_extruded_volume + extruded_volume; } _write_format(file, "; total filament cost = %.1lf\n", print.total_cost); - _write_format(file, "; estimated printing time = %s\n", m_time_estimator.get_time_hms().c_str()); + _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str()); + if (m_silent_time_estimator_enabled) + _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str()); // Append full config. _write(file, "\n"); @@ -1406,7 +1455,7 @@ void GCode::process_layer( if (m_pressure_equalizer) gcode = m_pressure_equalizer->process(gcode.c_str(), false); // printf("G-code after filter:\n%s\n", out.c_str()); - + _write(file, gcode); } @@ -2073,7 +2122,9 @@ void GCode::_write(FILE* file, const char *what) // writes string to file fwrite(gcode, 1, ::strlen(gcode), file); // updates time estimator and gcode lines vector - m_time_estimator.add_gcode_block(gcode); + m_normal_time_estimator.add_gcode_block(gcode); + if (m_silent_time_estimator_enabled) + m_silent_time_estimator.add_gcode_block(gcode); } } diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index a5c63f208..7bc525731 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -133,6 +133,9 @@ public: m_last_height(GCodeAnalyzer::Default_Height), m_brim_done(false), m_second_layer_things_done(false), + m_normal_time_estimator(GCodeTimeEstimator::Normal), + m_silent_time_estimator(GCodeTimeEstimator::Silent), + m_silent_time_estimator_enabled(false), m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max())) {} ~GCode() {} @@ -303,8 +306,10 @@ protected: // Index of a last object copy extruded. std::pair<const PrintObject*, Point> m_last_obj_copy; - // Time estimator - GCodeTimeEstimator m_time_estimator; + // Time estimators + GCodeTimeEstimator m_normal_time_estimator; + GCodeTimeEstimator m_silent_time_estimator; + bool m_silent_time_estimator_enabled; // Analyzer GCodeAnalyzer m_analyzer; diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index 176159ff5..6e500bd4b 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -4,18 +4,33 @@ #include <Shiny/Shiny.h> +#include <boost/nowide/fstream.hpp> +#include <boost/nowide/cstdio.hpp> +#include <boost/algorithm/string/predicate.hpp> + static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float MILLISEC_TO_SEC = 0.001f; static const float INCHES_TO_MM = 25.4f; -static const float DEFAULT_FEEDRATE = 1500.0f; // from Prusa Firmware (Marlin_main.cpp) -static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_AXIS_MAX_FEEDRATE[] = { 500.0f, 500.0f, 12.0f, 120.0f }; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_AXIS_MAX_ACCELERATION[] = { 9000.0f, 9000.0f, 500.0f, 10000.0f }; // Prusa Firmware 1_75mm_MK2 -static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.2f, 2.5f }; // from Prusa Firmware (Configuration.h) -static const float DEFAULT_MINIMUM_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) -static const float DEFAULT_MINIMUM_TRAVEL_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) -static const float DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 percent + +static const float NORMAL_FEEDRATE = 1500.0f; // from Prusa Firmware (Marlin_main.cpp) +static const float NORMAL_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 +static const float NORMAL_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 +static const float NORMAL_AXIS_MAX_FEEDRATE[] = { 500.0f, 500.0f, 12.0f, 120.0f }; // Prusa Firmware 1_75mm_MK2 +static const float NORMAL_AXIS_MAX_ACCELERATION[] = { 9000.0f, 9000.0f, 500.0f, 10000.0f }; // Prusa Firmware 1_75mm_MK2 +static const float NORMAL_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.4f, 2.5f }; // from Prusa Firmware (Configuration.h) +static const float NORMAL_MINIMUM_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) +static const float NORMAL_MINIMUM_TRAVEL_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) +static const float NORMAL_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 percent + +static const float SILENT_FEEDRATE = 1500.0f; // from Prusa Firmware (Marlin_main.cpp) +static const float SILENT_ACCELERATION = 1250.0f; // Prusa Firmware 1_75mm_MK25-RAMBo13a-E3Dv6full +static const float SILENT_RETRACT_ACCELERATION = 1250.0f; // Prusa Firmware 1_75mm_MK25-RAMBo13a-E3Dv6full +static const float SILENT_AXIS_MAX_FEEDRATE[] = { 200.0f, 200.0f, 12.0f, 120.0f }; // Prusa Firmware 1_75mm_MK25-RAMBo13a-E3Dv6full +static const float SILENT_AXIS_MAX_ACCELERATION[] = { 1000.0f, 1000.0f, 200.0f, 5000.0f }; // Prusa Firmware 1_75mm_MK25-RAMBo13a-E3Dv6full +static const float SILENT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.4f, 2.5f }; // from Prusa Firmware (Configuration.h) +static const float SILENT_MINIMUM_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) +static const float SILENT_MINIMUM_TRAVEL_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h) +static const float SILENT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 percent static const float PREVIOUS_FEEDRATE_THRESHOLD = 0.0001f; @@ -73,6 +88,11 @@ namespace Slic3r { return ::sqrt(value); } + GCodeTimeEstimator::Block::Block() + : st_synchronized(false) + { + } + float GCodeTimeEstimator::Block::move_length() const { float length = ::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); @@ -159,12 +179,45 @@ namespace Slic3r { } #endif // ENABLE_MOVE_STATS - GCodeTimeEstimator::GCodeTimeEstimator() + GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) + : _mode(mode) { reset(); set_default(); } + void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line) + { + PROFILE_FUNC(); + _parser.parse_line(gcode_line, + [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) + { this->_process_gcode_line(reader, line); }); + } + + void GCodeTimeEstimator::add_gcode_block(const char *ptr) + { + PROFILE_FUNC(); + GCodeReader::GCodeLine gline; + auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) + { this->_process_gcode_line(reader, line); }; + for (; *ptr != 0;) { + gline.reset(); + ptr = _parser.parse_line(ptr, gline, action); + } + } + + void GCodeTimeEstimator::calculate_time() + { + PROFILE_FUNC(); + _reset_time(); + _set_blocks_st_synchronize(false); + _calculate_time(); + +#if ENABLE_MOVE_STATS + _log_moves_stats(); +#endif // ENABLE_MOVE_STATS + } + void GCodeTimeEstimator::calculate_time_from_text(const std::string& gcode) { reset(); @@ -178,9 +231,6 @@ namespace Slic3r { #if ENABLE_MOVE_STATS _log_moves_stats(); #endif // ENABLE_MOVE_STATS - - _reset_blocks(); - _reset(); } void GCodeTimeEstimator::calculate_time_from_file(const std::string& file) @@ -193,9 +243,6 @@ namespace Slic3r { #if ENABLE_MOVE_STATS _log_moves_stats(); #endif // ENABLE_MOVE_STATS - - _reset_blocks(); - _reset(); } void GCodeTimeEstimator::calculate_time_from_lines(const std::vector<std::string>& gcode_lines) @@ -211,42 +258,118 @@ namespace Slic3r { #if ENABLE_MOVE_STATS _log_moves_stats(); #endif // ENABLE_MOVE_STATS - - _reset_blocks(); - _reset(); } - void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line) + bool GCodeTimeEstimator::post_process_remaining_times(const std::string& filename, float interval) { - PROFILE_FUNC(); - _parser.parse_line(gcode_line, - [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) - { this->_process_gcode_line(reader, line); }); - } + boost::nowide::ifstream in(filename); + if (!in.good()) + throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for reading.\n")); - void GCodeTimeEstimator::add_gcode_block(const char *ptr) - { - PROFILE_FUNC(); - GCodeReader::GCodeLine gline; - auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) - { this->_process_gcode_line(reader, line); }; - for (; *ptr != 0;) { - gline.reset(); - ptr = _parser.parse_line(ptr, gline, action); + std::string path_tmp = filename + ".times"; + + FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb"); + if (out == nullptr) + throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n")); + + std::string time_mask; + switch (_mode) + { + default: + case Normal: + { + time_mask = "M73 P%s R%s\n"; + break; + } + case Silent: + { + time_mask = "M73 Q%s S%s\n"; + break; + } } - } - void GCodeTimeEstimator::calculate_time() - { - PROFILE_FUNC(); - _calculate_time(); + unsigned int g1_lines_count = 0; + float last_recorded_time = 0.0f; + std::string gcode_line; + // buffer line to export only when greater than 64K to reduce writing calls + std::string export_line; + char time_line[64]; + while (std::getline(in, gcode_line)) + { + if (!in.good()) + { + fclose(out); + throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n")); + } -#if ENABLE_MOVE_STATS - _log_moves_stats(); -#endif // ENABLE_MOVE_STATS + gcode_line += "\n"; - _reset_blocks(); - _reset(); + // add remaining time lines where needed + _parser.parse_line(gcode_line, + [this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) + { + if (line.cmd_is("G1")) + { + ++g1_lines_count; + + if (!line.has_e()) + return; + + G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count); + if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size())) + { + const Block& block = _blocks[it->second]; + if (block.elapsed_time != -1.0f) + { + float block_remaining_time = _time - block.elapsed_time; + if (std::abs(last_recorded_time - block_remaining_time) > interval) + { + sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); + gcode_line += time_line; + + last_recorded_time = block_remaining_time; + } + } + } + } + }); + + export_line += gcode_line; + if (export_line.length() > 65535) + { + fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); + if (ferror(out)) + { + in.close(); + fclose(out); + boost::nowide::remove(path_tmp.c_str()); + throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); + } + export_line.clear(); + } + } + + if (export_line.length() > 0) + { + fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); + if (ferror(out)) + { + in.close(); + fclose(out); + boost::nowide::remove(path_tmp.c_str()); + throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); + } + } + + fclose(out); + in.close(); + + boost::nowide::remove(filename.c_str()); + if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0) + throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + + "Is " + path_tmp + " locked?" + '\n'); + + return true; } void GCodeTimeEstimator::set_axis_position(EAxis axis, float position) @@ -266,6 +389,24 @@ namespace Slic3r { void GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk) { + if ((axis == X) || (axis == Y)) + { + switch (_mode) + { + default: + case Normal: + { + jerk = std::min(jerk, NORMAL_AXIS_MAX_JERK[axis]); + break; + } + case Silent: + { + jerk = std::min(jerk, SILENT_AXIS_MAX_JERK[axis]); + break; + } + } + } + _state.axis[axis].max_jerk = jerk; } @@ -389,6 +530,21 @@ namespace Slic3r { return _state.e_local_positioning_type; } + int GCodeTimeEstimator::get_g1_line_id() const + { + return _state.g1_line_id; + } + + void GCodeTimeEstimator::increment_g1_line_id() + { + ++_state.g1_line_id; + } + + void GCodeTimeEstimator::reset_g1_line_id() + { + _state.g1_line_id = 0; + } + void GCodeTimeEstimator::add_additional_time(float timeSec) { _state.additional_time += timeSec; @@ -411,25 +567,25 @@ namespace Slic3r { set_global_positioning_type(Absolute); set_e_local_positioning_type(Absolute); - set_feedrate(DEFAULT_FEEDRATE); - set_acceleration(DEFAULT_ACCELERATION); - set_retract_acceleration(DEFAULT_RETRACT_ACCELERATION); - set_minimum_feedrate(DEFAULT_MINIMUM_FEEDRATE); - set_minimum_travel_feedrate(DEFAULT_MINIMUM_TRAVEL_FEEDRATE); - set_extrude_factor_override_percentage(DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE); - - for (unsigned char a = X; a < Num_Axis; ++a) + switch (_mode) { - EAxis axis = (EAxis)a; - set_axis_max_feedrate(axis, DEFAULT_AXIS_MAX_FEEDRATE[a]); - set_axis_max_acceleration(axis, DEFAULT_AXIS_MAX_ACCELERATION[a]); - set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]); + default: + case Normal: + { + _set_default_as_normal(); + break; + } + case Silent: + { + _set_default_as_silent(); + break; + } } } void GCodeTimeEstimator::reset() { - _time = 0.0f; + _reset_time(); #if ENABLE_MOVE_STATS _moves_stats.clear(); #endif // ENABLE_MOVE_STATS @@ -442,23 +598,14 @@ namespace Slic3r { return _time; } - std::string GCodeTimeEstimator::get_time_hms() const + std::string GCodeTimeEstimator::get_time_dhms() const { - float timeinsecs = get_time(); - int hours = (int)(timeinsecs / 3600.0f); - timeinsecs -= (float)hours * 3600.0f; - int minutes = (int)(timeinsecs / 60.0f); - timeinsecs -= (float)minutes * 60.0f; - - char buffer[64]; - if (hours > 0) - ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)timeinsecs); - else if (minutes > 0) - ::sprintf(buffer, "%dm %ds", minutes, (int)timeinsecs); - else - ::sprintf(buffer, "%ds", (int)timeinsecs); + return _get_time_dhms(get_time()); + } - return buffer; + std::string GCodeTimeEstimator::get_time_minutes() const + { + return _get_time_minutes(get_time()); } void GCodeTimeEstimator::_reset() @@ -471,6 +618,14 @@ namespace Slic3r { set_axis_position(Z, 0.0f); set_additional_time(0.0f); + + reset_g1_line_id(); + _g1_line_ids.clear(); + } + + void GCodeTimeEstimator::_reset_time() + { + _time = 0.0f; } void GCodeTimeEstimator::_reset_blocks() @@ -478,6 +633,50 @@ namespace Slic3r { _blocks.clear(); } + void GCodeTimeEstimator::_set_default_as_normal() + { + set_feedrate(NORMAL_FEEDRATE); + set_acceleration(NORMAL_ACCELERATION); + set_retract_acceleration(NORMAL_RETRACT_ACCELERATION); + set_minimum_feedrate(NORMAL_MINIMUM_FEEDRATE); + set_minimum_travel_feedrate(NORMAL_MINIMUM_TRAVEL_FEEDRATE); + set_extrude_factor_override_percentage(NORMAL_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE); + + for (unsigned char a = X; a < Num_Axis; ++a) + { + EAxis axis = (EAxis)a; + set_axis_max_feedrate(axis, NORMAL_AXIS_MAX_FEEDRATE[a]); + set_axis_max_acceleration(axis, NORMAL_AXIS_MAX_ACCELERATION[a]); + set_axis_max_jerk(axis, NORMAL_AXIS_MAX_JERK[a]); + } + } + + void GCodeTimeEstimator::_set_default_as_silent() + { + set_feedrate(SILENT_FEEDRATE); + set_acceleration(SILENT_ACCELERATION); + set_retract_acceleration(SILENT_RETRACT_ACCELERATION); + set_minimum_feedrate(SILENT_MINIMUM_FEEDRATE); + set_minimum_travel_feedrate(SILENT_MINIMUM_TRAVEL_FEEDRATE); + set_extrude_factor_override_percentage(SILENT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE); + + for (unsigned char a = X; a < Num_Axis; ++a) + { + EAxis axis = (EAxis)a; + set_axis_max_feedrate(axis, SILENT_AXIS_MAX_FEEDRATE[a]); + set_axis_max_acceleration(axis, SILENT_AXIS_MAX_ACCELERATION[a]); + set_axis_max_jerk(axis, SILENT_AXIS_MAX_JERK[a]); + } + } + + void GCodeTimeEstimator::_set_blocks_st_synchronize(bool state) + { + for (Block& block : _blocks) + { + block.st_synchronized = state; + } + } + void GCodeTimeEstimator::_calculate_time() { _forward_pass(); @@ -486,14 +685,18 @@ namespace Slic3r { _time += get_additional_time(); - for (const Block& block : _blocks) + for (Block& block : _blocks) { + if (block.st_synchronized) + continue; + #if ENABLE_MOVE_STATS float block_time = 0.0f; block_time += block.acceleration_time(); block_time += block.cruise_time(); block_time += block.deceleration_time(); _time += block_time; + block.elapsed_time = _time; MovesStatsMap::iterator it = _moves_stats.find(block.move_type); if (it == _moves_stats.end()) @@ -505,6 +708,7 @@ namespace Slic3r { _time += block.acceleration_time(); _time += block.cruise_time(); _time += block.deceleration_time(); + block.elapsed_time = _time; #endif // ENABLE_MOVE_STATS } } @@ -642,6 +846,8 @@ namespace Slic3r { void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line) { + increment_g1_line_id(); + // updates axes positions from line EUnits units = get_units(); float new_pos[Num_Axis]; @@ -829,6 +1035,7 @@ namespace Slic3r { // adds block to blocks list _blocks.emplace_back(block); + _g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); } void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) @@ -1043,7 +1250,7 @@ namespace Slic3r { void GCodeTimeEstimator::_simulate_st_synchronize() { _calculate_time(); - _reset_blocks(); + _set_blocks_st_synchronize(true); } void GCodeTimeEstimator::_forward_pass() @@ -1051,7 +1258,10 @@ namespace Slic3r { if (_blocks.size() > 1) { for (unsigned int i = 0; i < (unsigned int)_blocks.size() - 1; ++i) - { + { + if (_blocks[i].st_synchronized || _blocks[i + 1].st_synchronized) + continue; + _planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]); } } @@ -1063,6 +1273,9 @@ namespace Slic3r { { for (int i = (int)_blocks.size() - 1; i >= 1; --i) { + if (_blocks[i - 1].st_synchronized || _blocks[i].st_synchronized) + continue; + _planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]); } } @@ -1115,6 +1328,9 @@ namespace Slic3r { for (Block& b : _blocks) { + if (b.st_synchronized) + continue; + curr = next; next = &b; @@ -1144,6 +1360,33 @@ namespace Slic3r { } } + std::string GCodeTimeEstimator::_get_time_dhms(float time_in_secs) + { + int days = (int)(time_in_secs / 86400.0f); + time_in_secs -= (float)days * 86400.0f; + int hours = (int)(time_in_secs / 3600.0f); + time_in_secs -= (float)hours * 3600.0f; + int minutes = (int)(time_in_secs / 60.0f); + time_in_secs -= (float)minutes * 60.0f; + + char buffer[64]; + if (days > 0) + ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs); + else if (hours > 0) + ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs); + else if (minutes > 0) + ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs); + else + ::sprintf(buffer, "%ds", (int)time_in_secs); + + return buffer; + } + + std::string GCodeTimeEstimator::_get_time_minutes(float time_in_secs) + { + return std::to_string((int)(::roundf(time_in_secs / 60.0f))); + } + #if ENABLE_MOVE_STATS void GCodeTimeEstimator::_log_moves_stats() const { diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp index 8f948abd1..0307b658e 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.hpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp @@ -17,6 +17,12 @@ namespace Slic3r { class GCodeTimeEstimator { public: + enum EMode : unsigned char + { + Normal, + Silent + }; + enum EUnits : unsigned char { Millimeters, @@ -70,7 +76,8 @@ namespace Slic3r { float additional_time; // s float minimum_feedrate; // mm/s float minimum_travel_feedrate; // mm/s - float extrude_factor_override_percentage; + float extrude_factor_override_percentage; + unsigned int g1_line_id; }; public: @@ -121,7 +128,6 @@ namespace Slic3r { bool nominal_length; }; - #if ENABLE_MOVE_STATS EMoveType move_type; #endif // ENABLE_MOVE_STATS @@ -134,6 +140,11 @@ namespace Slic3r { FeedrateProfile feedrate; Trapezoid trapezoid; + float elapsed_time; + + bool st_synchronized; + + Block(); // Returns the length of the move covered by this block, in mm float move_length() const; @@ -187,19 +198,34 @@ namespace Slic3r { typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap; #endif // ENABLE_MOVE_STATS + typedef std::map<unsigned int, unsigned int> G1LineIdToBlockIdMap; + private: + EMode _mode; GCodeReader _parser; State _state; Feedrates _curr; Feedrates _prev; BlocksList _blocks; + // Map between g1 line id and blocks id, used to speed up export of remaining times + G1LineIdToBlockIdMap _g1_line_ids; float _time; // s + #if ENABLE_MOVE_STATS MovesStatsMap _moves_stats; #endif // ENABLE_MOVE_STATS public: - GCodeTimeEstimator(); + explicit GCodeTimeEstimator(EMode mode); + + // Adds the given gcode line + void add_gcode_line(const std::string& gcode_line); + + void add_gcode_block(const char *ptr); + void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); } + + // Calculates the time estimate from the gcode lines added using add_gcode_line() or add_gcode_block() + void calculate_time(); // Calculates the time estimate from the given gcode in string format void calculate_time_from_text(const std::string& gcode); @@ -210,14 +236,12 @@ namespace Slic3r { // Calculates the time estimate from the gcode contained in given list of gcode lines void calculate_time_from_lines(const std::vector<std::string>& gcode_lines); - // Adds the given gcode line - void add_gcode_line(const std::string& gcode_line); - - void add_gcode_block(const char *ptr); - void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); } - - // Calculates the time estimate from the gcode lines added using add_gcode_line() - void calculate_time(); + // Process the gcode contained in the file with the given filename, + // placing in it new lines (M73) containing the remaining time, at the given interval in seconds + // and saving the result back in the same file + // This time estimator should have been already used to calculate the time estimate for the gcode + // contained in the given file before to call this method + bool post_process_remaining_times(const std::string& filename, float interval_sec); // Set current position on the given axis with the given value void set_axis_position(EAxis axis, float position); @@ -263,6 +287,10 @@ namespace Slic3r { void set_e_local_positioning_type(EPositioningType type); EPositioningType get_e_local_positioning_type() const; + int get_g1_line_id() const; + void increment_g1_line_id(); + void reset_g1_line_id(); + void add_additional_time(float timeSec); void set_additional_time(float timeSec); float get_additional_time() const; @@ -275,13 +303,22 @@ namespace Slic3r { // Returns the estimated time, in seconds float get_time() const; - // Returns the estimated time, in format HHh MMm SSs - std::string get_time_hms() const; + // Returns the estimated time, in format DDd HHh MMm SSs + std::string get_time_dhms() const; + + // Returns the estimated time, in minutes (integer) + std::string get_time_minutes() const; private: void _reset(); + void _reset_time(); void _reset_blocks(); + void _set_default_as_normal(); + void _set_default_as_silent(); + + void _set_blocks_st_synchronize(bool state); + // Calculates the time estimate void _calculate_time(); @@ -353,6 +390,12 @@ namespace Slic3r { void _recalculate_trapezoids(); + // Returns the given time is seconds in format DDd HHh MMm SSs + static std::string _get_time_dhms(float time_in_secs); + + // Returns the given, in minutes (integer) + static std::string _get_time_minutes(float time_in_secs); + #if ENABLE_MOVE_STATS void _log_moves_stats() const; #endif // ENABLE_MOVE_STATS diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 3ea7ffb68..2217547ea 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -236,7 +236,8 @@ public: PrintRegionPtrs regions; PlaceholderParser placeholder_parser; // TODO: status_cb - std::string estimated_print_time; + std::string estimated_normal_print_time; + std::string estimated_silent_print_time; double total_used_filament, total_extruded_volume, total_cost, total_weight; std::map<size_t, float> filament_stats; PrintState<PrintStep, psCount> state; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6f3cdc04d..f541089a3 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -291,11 +291,11 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); - def->enum_labels.push_back("Rectilinear"); - def->enum_labels.push_back("Concentric"); - def->enum_labels.push_back("Hilbert Curve"); - def->enum_labels.push_back("Archimedean Chords"); - def->enum_labels.push_back("Octagram Spiral"); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Hilbert Curve")); + def->enum_labels.push_back(L("Archimedean Chords")); + def->enum_labels.push_back(L("Octagram Spiral")); // solid_fill_pattern is an obsolete equivalent to external_fill_pattern. def->aliases.push_back("solid_fill_pattern"); def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear); @@ -651,19 +651,19 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); - def->enum_labels.push_back("Rectilinear"); - def->enum_labels.push_back("Grid"); - def->enum_labels.push_back("Triangles"); - def->enum_labels.push_back("Stars"); - def->enum_labels.push_back("Cubic"); - def->enum_labels.push_back("Line"); - def->enum_labels.push_back("Concentric"); - def->enum_labels.push_back("Honeycomb"); - def->enum_labels.push_back("3D Honeycomb"); - def->enum_labels.push_back("Gyroid"); - def->enum_labels.push_back("Hilbert Curve"); - def->enum_labels.push_back("Archimedean Chords"); - def->enum_labels.push_back("Octagram Spiral"); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Grid")); + def->enum_labels.push_back(L("Triangles")); + def->enum_labels.push_back(L("Stars")); + def->enum_labels.push_back(L("Cubic")); + def->enum_labels.push_back(L("Line")); + def->enum_labels.push_back(L("Concentric")); + def->enum_labels.push_back(L("Honeycomb")); + def->enum_labels.push_back(L("3D Honeycomb")); + def->enum_labels.push_back(L("Gyroid")); + def->enum_labels.push_back(L("Hilbert Curve")); + def->enum_labels.push_back(L("Archimedean Chords")); + def->enum_labels.push_back(L("Octagram Spiral")); def->default_value = new ConfigOptionEnum<InfillPattern>(ipStars); def = this->add("first_layer_acceleration", coFloat); @@ -771,7 +771,7 @@ PrintConfigDef::PrintConfigDef() def->enum_labels.push_back("Mach3/LinuxCNC"); def->enum_labels.push_back("Machinekit"); def->enum_labels.push_back("Smoothie"); - def->enum_labels.push_back("No extrusion"); + def->enum_labels.push_back(L("No extrusion")); def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfMarlin); def = this->add("infill_acceleration", coFloat); @@ -892,6 +892,12 @@ PrintConfigDef::PrintConfigDef() def->min = 0; def->default_value = new ConfigOptionFloat(0.3); + def = this->add("silent_mode", coBool); + def->label = L("Support silent mode"); + def->tooltip = L("Set silent mode for the G-code flavor"); + def->default_value = new ConfigOptionBool(true); + + const int machine_limits_opt_width = 70; { struct AxisDefault { std::string name; @@ -901,75 +907,82 @@ PrintConfigDef::PrintConfigDef() }; std::vector<AxisDefault> axes { // name, max_feedrate, max_acceleration, max_jerk - { "x", { 200., 200. }, { 1000., 1000. }, { 10., 10. } }, - { "y", { 200., 200. }, { 1000., 1000. }, { 10., 10. } }, - { "z", { 12., 12. }, { 200., 200. }, { 0.4, 0.4 } }, - { "e", { 120., 120. }, { 5000., 5000. }, { 2.5, 2.5 } } + { "x", { 500., 200. }, { 9000., 1000. }, { 10., 10. } }, + { "y", { 500., 200. }, { 9000., 1000. }, { 10., 10. } }, + { "z", { 12., 12. }, { 500., 200. }, { 0.2, 0.4 } }, + { "e", { 120., 120. }, { 10000., 5000. }, { 2.5, 2.5 } } }; for (const AxisDefault &axis : axes) { std::string axis_upper = boost::to_upper_copy<std::string>(axis.name); // Add the machine feedrate limits for XYZE axes. (M203) def = this->add("machine_max_feedrate_" + axis.name, coFloats); - def->label = (boost::format(L("Maximum feedrate %1%")) % axis_upper).str(); + def->full_label = (boost::format(L("Maximum feedrate %1%")) % axis_upper).str(); def->category = L("Machine limits"); def->tooltip = (boost::format(L("Maximum feedrate of the %1% axis")) % axis_upper).str(); def->sidetext = L("mm/s"); def->min = 0; + def->width = machine_limits_opt_width; def->default_value = new ConfigOptionFloats(axis.max_feedrate); // Add the machine acceleration limits for XYZE axes (M201) def = this->add("machine_max_acceleration_" + axis.name, coFloats); - def->label = (boost::format(L("Maximum acceleration %1%")) % axis_upper).str(); + def->full_label = (boost::format(L("Maximum acceleration %1%")) % axis_upper).str(); def->category = L("Machine limits"); def->tooltip = (boost::format(L("Maximum acceleration of the %1% axis")) % axis_upper).str(); def->sidetext = L("mm/s²"); def->min = 0; + def->width = machine_limits_opt_width; def->default_value = new ConfigOptionFloats(axis.max_acceleration); // Add the machine jerk limits for XYZE axes (M205) def = this->add("machine_max_jerk_" + axis.name, coFloats); - def->label = (boost::format(L("Maximum jerk %1%")) % axis_upper).str(); + def->full_label = (boost::format(L("Maximum jerk %1%")) % axis_upper).str(); def->category = L("Machine limits"); def->tooltip = (boost::format(L("Maximum jerk of the %1% axis")) % axis_upper).str(); def->sidetext = L("mm/s"); def->min = 0; + def->width = machine_limits_opt_width; def->default_value = new ConfigOptionFloats(axis.max_jerk); } } // M205 S... [mm/sec] def = this->add("machine_min_extruding_rate", coFloats); - def->label = L("Minimum feedrate when extruding"); + def->full_label = L("Minimum feedrate when extruding"); def->category = L("Machine limits"); def->tooltip = L("Minimum feedrate when extruding") + " (M205 S)"; def->sidetext = L("mm/s"); def->min = 0; - def->default_value = new ConfigOptionFloats(0., 0.); + def->width = machine_limits_opt_width; + def->default_value = new ConfigOptionFloats{ 0., 0. }; // M205 T... [mm/sec] def = this->add("machine_min_travel_rate", coFloats); - def->label = L("Minimum travel feedrate"); + def->full_label = L("Minimum travel feedrate"); def->category = L("Machine limits"); def->tooltip = L("Minimum travel feedrate") + " (M205 T)"; def->sidetext = L("mm/s"); def->min = 0; - def->default_value = new ConfigOptionFloats(0., 0.); + def->width = machine_limits_opt_width; + def->default_value = new ConfigOptionFloats{ 0., 0. }; // M204 S... [mm/sec^2] def = this->add("machine_max_acceleration_extruding", coFloats); - def->label = L("Maximum acceleration when extruding"); + def->full_label = L("Maximum acceleration when extruding"); def->category = L("Machine limits"); def->tooltip = L("Maximum acceleration when extruding") + " (M204 S)"; def->sidetext = L("mm/s²"); def->min = 0; - def->default_value = new ConfigOptionFloats(1250., 1250.); + def->width = machine_limits_opt_width; + def->default_value = new ConfigOptionFloats{ 1500., 1250. }; // M204 T... [mm/sec^2] def = this->add("machine_max_acceleration_retracting", coFloats); - def->label = L("Maximum acceleration when retracting"); + def->full_label = L("Maximum acceleration when retracting"); def->category = L("Machine limits"); def->tooltip = L("Maximum acceleration when retracting") + " (M204 T)"; def->sidetext = L("mm/s²"); def->min = 0; - def->default_value = new ConfigOptionFloats(1250., 1250.); + def->width = machine_limits_opt_width; + def->default_value = new ConfigOptionFloats{ 1500., 1250. }; def = this->add("max_fan_speed", coInts); def->label = L("Max"); @@ -1392,10 +1405,10 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("nearest"); def->enum_values.push_back("aligned"); def->enum_values.push_back("rear"); - def->enum_labels.push_back("Random"); - def->enum_labels.push_back("Nearest"); - def->enum_labels.push_back("Aligned"); - def->enum_labels.push_back("Rear"); + def->enum_labels.push_back(L("Random")); + def->enum_labels.push_back(L("Nearest")); + def->enum_labels.push_back(L("Aligned")); + def->enum_labels.push_back(L("Rear")); def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned); #if 0 @@ -1608,7 +1621,7 @@ PrintConfigDef::PrintConfigDef() def->label = L("Single Extruder Multi Material"); def->tooltip = L("The printer multiplexes filaments into a single hot end."); def->cli = "single-extruder-multi-material!"; - def->default_value = new ConfigOptionBool(false); + def->default_value = new ConfigOptionBool(false); def = this->add("support_material", coBool); def->label = L("Generate support material"); @@ -1658,8 +1671,8 @@ PrintConfigDef::PrintConfigDef() def->min = 0; def->enum_values.push_back("0"); def->enum_values.push_back("0.2"); - def->enum_labels.push_back("0 (soluble)"); - def->enum_labels.push_back("0.2 (detachable)"); + def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str()); + def->enum_labels.push_back((boost::format("0.2 (%1%)") % L("detachable")).str()); def->default_value = new ConfigOptionFloat(0.2); def = this->add("support_material_enforce_layers", coInt); @@ -1748,9 +1761,9 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("rectilinear"); def->enum_values.push_back("rectilinear-grid"); def->enum_values.push_back("honeycomb"); - def->enum_labels.push_back("rectilinear"); - def->enum_labels.push_back("rectilinear grid"); - def->enum_labels.push_back("honeycomb"); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Rectilinear grid")); + def->enum_labels.push_back(L("Honeycomb")); def->default_value = new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear); def = this->add("support_material_spacing", coFloat); diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 6a03c3a04..c530868a1 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -562,9 +562,9 @@ public: ConfigOptionFloat cooling_tube_retraction; ConfigOptionFloat cooling_tube_length; ConfigOptionFloat parking_pos_retraction; + ConfigOptionBool silent_mode; ConfigOptionFloat extra_loading_move; - std::string get_extrusion_axis() const { return @@ -623,6 +623,7 @@ protected: OPT_PTR(cooling_tube_retraction); OPT_PTR(cooling_tube_length); OPT_PTR(parking_pos_retraction); + OPT_PTR(silent_mode); OPT_PTR(extra_loading_move); } }; @@ -676,6 +677,7 @@ public: ConfigOptionString output_filename_format; ConfigOptionFloat perimeter_acceleration; ConfigOptionStrings post_process; + ConfigOptionString printer_model; ConfigOptionString printer_notes; ConfigOptionFloat resolution; ConfigOptionFloats retract_before_travel; @@ -746,6 +748,7 @@ protected: OPT_PTR(output_filename_format); OPT_PTR(perimeter_acceleration); OPT_PTR(post_process); + OPT_PTR(printer_model); OPT_PTR(printer_notes); OPT_PTR(resolution); OPT_PTR(retract_before_travel); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index a52946afb..d8fe592e8 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1686,7 +1686,7 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con m_data.clear(); // collects items to render - auto title = GUI::L_str(preview_data.get_legend_title()); + auto title = _(preview_data.get_legend_title()); const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors); unsigned int items_count = (unsigned int)items.size(); diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp index 85247b41b..36a1c396f 100644 --- a/xs/src/slic3r/GUI/Field.cpp +++ b/xs/src/slic3r/GUI/Field.cpp @@ -45,6 +45,22 @@ namespace Slic3r { namespace GUI { set_undo_bitmap(&bmp); set_undo_to_sys_bitmap(&bmp); + switch (m_opt.type) + { + case coPercents: + case coFloats: + case coStrings: + case coBools: + case coInts: { + auto tag_pos = m_opt_id.find("#"); + if (tag_pos != std::string::npos) + m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size())); + break; + } + default: + break; + } + BUILD(); } @@ -77,7 +93,7 @@ namespace Slic3r { namespace GUI { wxString Field::get_tooltip_text(const wxString& default_string) { wxString tooltip_text(""); - wxString tooltip = L_str(m_opt.tooltip); + wxString tooltip = _(m_opt.tooltip); if (tooltip.length() > 0) tooltip_text = tooltip + "(" + _(L("default")) + ": " + (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + @@ -161,10 +177,10 @@ namespace Slic3r { namespace GUI { case coFloat: { double val = m_opt.type == coFloats ? - static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(0) : + static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(m_opt_idx) : m_opt.type == coFloat ? m_opt.default_value->getFloat() : - static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(0); + static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(m_opt_idx); text_value = double_to_string(val); break; } @@ -174,10 +190,8 @@ namespace Slic3r { namespace GUI { case coStrings: { const ConfigOptionStrings *vec = static_cast<const ConfigOptionStrings*>(m_opt.default_value); - if (vec == nullptr || vec->empty()) break; - if (vec->size() > 1) - break; - text_value = vec->values.at(0); + if (vec == nullptr || vec->empty()) break; //for the case of empty default value + text_value = vec->get_at(m_opt_idx); break; } default: @@ -259,7 +273,7 @@ void CheckBox::BUILD() { bool check_value = m_opt.type == coBool ? m_opt.default_value->getBool() : m_opt.type == coBools ? - static_cast<ConfigOptionBools*>(m_opt.default_value)->values.at(0) : + static_cast<ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) : false; auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size); @@ -365,7 +379,7 @@ void Choice::BUILD() { } else{ for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels){ - const wxString& str = m_opt_id == "support" ? L_str(el) : el; + const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el; temp->Append(str); } set_selection(); @@ -418,7 +432,7 @@ void Choice::set_selection() break; } case coStrings:{ - text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->values.at(0); + text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx); size_t idx = 0; for (auto el : m_opt.enum_values) @@ -582,7 +596,7 @@ void ColourPicker::BUILD() if (m_opt.height >= 0) size.SetHeight(m_opt.height); if (m_opt.width >= 0) size.SetWidth(m_opt.width); - wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->values.at(0)); + wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx)); auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size); // // recast as a wxWindow to fit the calling convention @@ -675,6 +689,22 @@ boost::any& PointCtrl::get_value() return m_value = ret_point; } +void StaticText::BUILD() +{ + auto size = wxSize(wxDefaultSize); + if (m_opt.height >= 0) size.SetHeight(m_opt.height); + if (m_opt.width >= 0) size.SetWidth(m_opt.width); + + wxString legend(static_cast<ConfigOptionString*>(m_opt.default_value)->value); + auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size); + temp->SetFont(bold_font()); + + // // recast as a wxWindow to fit the calling convention + window = dynamic_cast<wxWindow*>(temp); + + temp->SetToolTip(get_tooltip_text(legend)); +} + } // GUI } // Slic3r diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp index 948178d3e..db8d2a408 100644 --- a/xs/src/slic3r/GUI/Field.hpp +++ b/xs/src/slic3r/GUI/Field.hpp @@ -95,6 +95,7 @@ public: /// Copy of ConfigOption for deduction purposes const ConfigOptionDef m_opt {ConfigOptionDef()}; const t_config_option_key m_opt_id;//! {""}; + int m_opt_idx = 0; /// Sets a value for this control. /// subclasses should overload with a specific version @@ -384,6 +385,34 @@ public: wxSizer* getSizer() override { return sizer; } }; +class StaticText : public Field { + using Field::Field; +public: + StaticText(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} + StaticText(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} + ~StaticText() {} + + wxWindow* window{ nullptr }; + void BUILD() override; + + void set_value(const std::string& value, bool change_event = false) { + m_disable_change_event = !change_event; + dynamic_cast<wxStaticText*>(window)->SetLabel(value); + m_disable_change_event = false; + } + void set_value(const boost::any& value, bool change_event = false) { + m_disable_change_event = !change_event; + dynamic_cast<wxStaticText*>(window)->SetLabel(boost::any_cast<wxString>(value)); + m_disable_change_event = false; + } + + boost::any& get_value()override { return m_value; } + + void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); }; + void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); }; + wxWindow* getWindow() override { return window; } +}; + } // GUI } // Slic3r diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index b27da33c8..af7022f2b 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -56,8 +56,9 @@ #include "../Utils/PresetUpdater.hpp" #include "../Config/Snapshot.hpp" -#include "libslic3r/I18N.hpp" + #include "3DScene.hpp" +#include "libslic3r/I18N.hpp" namespace Slic3r { namespace GUI { @@ -110,6 +111,7 @@ wxNotebook *g_wxTabPanel = nullptr; AppConfig *g_AppConfig = nullptr; PresetBundle *g_PresetBundle= nullptr; PresetUpdater *g_PresetUpdater = nullptr; +_3DScene *g_3DScene = nullptr; wxColour g_color_label_modified; wxColour g_color_label_sys; wxColour g_color_label_default; @@ -118,6 +120,9 @@ std::vector<Tab *> g_tabs_list; wxLocale* g_wxLocale; +wxFont g_small_font; +wxFont g_bold_font; + std::shared_ptr<ConfigOptionsGroup> m_optgroup; double m_brim_width = 0.0; wxButton* g_wiping_dialog_button = nullptr; @@ -150,6 +155,16 @@ void update_label_colours_from_appconfig() } } +static void init_fonts() +{ + g_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + g_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); +#ifdef __WXMAC__ + g_small_font.SetPointSize(11); + g_bold_font.SetPointSize(13); +#endif /*__WXMAC__*/ +} + static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } void set_wxapp(wxApp *app) @@ -158,6 +173,7 @@ void set_wxapp(wxApp *app) // Let the libslic3r know the callback, which will translate messages on demand. Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); init_label_colours(); + init_fonts(); } void set_main_frame(wxFrame *main_frame) @@ -185,6 +201,11 @@ void set_preset_updater(PresetUpdater *updater) g_PresetUpdater = updater; } +void set_3DScene(_3DScene *scene) +{ + g_3DScene = scene; +} + std::vector<Tab *>& get_tabs_list() { return g_tabs_list; @@ -675,6 +696,14 @@ void set_label_clr_sys(const wxColour& clr) { g_AppConfig->save(); } +const wxFont& small_font(){ + return g_small_font; +} + +const wxFont& bold_font(){ + return g_bold_font; +} + const wxColour& get_label_clr_default() { return g_color_label_default; } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 83052cb6e..efb11b7df 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -11,7 +11,7 @@ class wxApp; class wxWindow; class wxFrame; -class wxWindow; +class wxFont; class wxMenuBar; class wxNotebook; class wxComboCtrl; @@ -32,6 +32,7 @@ class AppConfig; class PresetUpdater; class DynamicPrintConfig; class TabIface; +class _3DScene; #define _(s) Slic3r::GUI::I18N::translate((s)) @@ -90,6 +91,7 @@ void set_tab_panel(wxNotebook *tab_panel); void set_app_config(AppConfig *app_config); void set_preset_bundle(PresetBundle *preset_bundle); void set_preset_updater(PresetUpdater *updater); +void set_3DScene(_3DScene *scene); AppConfig* get_app_config(); wxApp* get_app(); @@ -102,6 +104,9 @@ unsigned get_colour_approx_luma(const wxColour &colour); void set_label_clr_modified(const wxColour& clr); void set_label_clr_sys(const wxColour& clr); +const wxFont& small_font(); +const wxFont& bold_font(); + extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); // This is called when closing the application, when loading a config file or when starting the config wizard diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp index 629a9f3a0..d5cc29e19 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ b/xs/src/slic3r/GUI/OptionsGroup.cpp @@ -31,6 +31,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); } else if (opt.gui_type.compare("slider") == 0) { } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl + } else if (opt.gui_type.compare("legend") == 0) { // StaticText + m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id))); } else { switch (opt.type) { case coFloatOrPercent: @@ -86,7 +88,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co if (!this->m_disabled) this->back_to_sys_value(opt_id); }; - if (!m_is_tab_opt) { + if (!m_show_modified_btns) { field->m_Undo_btn->Hide(); field->m_Undo_to_sys_btn->Hide(); } @@ -199,7 +201,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* ConfigOptionDef option = opt.opt; // add label if any if (option.label != "") { - wxString str_label = L_str(option.label); + wxString str_label = _(option.label); //! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 // wxString str_label = (option.label == "Top" || option.label == "Bottom") ? // wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label.c_str()): @@ -220,7 +222,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* // add sidetext if any if (option.sidetext != "") { - auto sidetext = new wxStaticText(parent(), wxID_ANY, L_str(option.sidetext), wxDefaultPosition, wxDefaultSize); + auto sidetext = new wxStaticText(parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxDefaultSize); sidetext->SetFont(sidetext_font); sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); } @@ -242,7 +244,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* } Line OptionsGroup::create_single_option_line(const Option& option) const { - Line retval{ L_str(option.opt.label), L_str(option.opt.tooltip) }; + Line retval{ _(option.opt.label), _(option.opt.tooltip) }; Option tmp(option); tmp.opt.label = std::string(""); retval.append_option(tmp); diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp index 83b5b1233..422e1afd9 100644 --- a/xs/src/slic3r/GUI/OptionsGroup.hpp +++ b/xs/src/slic3r/GUI/OptionsGroup.hpp @@ -127,9 +127,15 @@ public: inline void enable() { for (auto& field : m_fields) field.second->enable(); } inline void disable() { for (auto& field : m_fields) field.second->disable(); } + void set_show_modified_btns_val(bool show) { + m_show_modified_btns = show; + } + OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) : - m_parent(_parent), title(title), m_is_tab_opt(is_tab_opt), staticbox(title!="") { - sizer = (staticbox ? new wxStaticBoxSizer(new wxStaticBox(_parent, wxID_ANY, title), wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); + m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), staticbox(title!="") { + auto stb = new wxStaticBox(_parent, wxID_ANY, title); + stb->SetFont(bold_font()); + sizer = (staticbox ? new wxStaticBoxSizer(stb/*new wxStaticBox(_parent, wxID_ANY, title)*/, wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); auto num_columns = 1U; if (label_width != 0) num_columns++; if (extra_column != nullptr) num_columns++; @@ -156,7 +162,7 @@ protected: bool m_disabled {false}; wxGridSizer* m_grid_sizer {nullptr}; // "true" if option is created in preset tabs - bool m_is_tab_opt{ false }; + bool m_show_modified_btns{ false }; // This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox // Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp index f3cedd693..91a2aa7bd 100644 --- a/xs/src/slic3r/GUI/Preset.cpp +++ b/xs/src/slic3r/GUI/Preset.cpp @@ -327,6 +327,11 @@ const std::vector<std::string>& Preset::printer_options() "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits", + "silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting", + "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", + "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", + "machine_min_extruding_rate", "machine_min_travel_rate", + "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e" }; s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); } diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index aa1c9c124..911ec0cbe 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -1605,6 +1605,22 @@ void TabPrinter::build() optgroup = page->new_optgroup(_(L("Firmware"))); optgroup->append_single_option_line("gcode_flavor"); + optgroup->append_single_option_line("silent_mode"); + + optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){ + wxTheApp->CallAfter([this, opt_key, value](){ + if (opt_key.compare("silent_mode") == 0) { + bool val = boost::any_cast<bool>(value); + if (m_use_silent_mode != val) { + m_rebuild_kinematics_page = true; + m_use_silent_mode = val; + } + } + build_extruder_pages(); + update_dirty(); + on_value_change(opt_key, value); + }); + }; optgroup = page->new_optgroup(_(L("Advanced"))); optgroup->append_single_option_line("use_relative_e_distances"); @@ -1686,8 +1702,94 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){ on_value_change("extruders_count", extruders_count); } -void TabPrinter::build_extruder_pages(){ +void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key) +{ + auto option = optgroup->get_option(opt_key, 0); + auto line = Line{ option.opt.full_label, "" }; + line.append_option(option); + if (m_use_silent_mode) + line.append_option(optgroup->get_option(opt_key, 1)); + optgroup->append_line(line); +} + +PageShp TabPrinter::build_kinematics_page() +{ + auto page = add_options_page(_(L("Machine limits")), "cog.png", true); + + if (m_use_silent_mode) { + // Legend for OptionsGroups + auto optgroup = page->new_optgroup(_(L(""))); + optgroup->set_show_modified_btns_val(false); + optgroup->label_width = 230; + auto line = Line{ "", "" }; + + ConfigOptionDef def; + def.type = coString; + def.width = 150; + def.gui_type = "legend"; + def.tooltip = L("Values in this column are for Full Power mode"); + def.default_value = new ConfigOptionString{ L("Full Power") }; + + auto option = Option(def, "full_power_legend"); + line.append_option(option); + + def.tooltip = L("Values in this column are for Silent mode"); + def.default_value = new ConfigOptionString{ L("Silent") }; + option = Option(def, "silent_legend"); + line.append_option(option); + + optgroup->append_line(line); + } + + std::vector<std::string> axes{ "x", "y", "z", "e" }; + auto optgroup = page->new_optgroup(_(L("Maximum accelerations"))); + for (const std::string &axis : axes) { + append_option_line(optgroup, "machine_max_acceleration_" + axis); + } + + optgroup = page->new_optgroup(_(L("Maximum feedrates"))); + for (const std::string &axis : axes) { + append_option_line(optgroup, "machine_max_feedrate_" + axis); + } + + optgroup = page->new_optgroup(_(L("Starting Acceleration"))); + append_option_line(optgroup, "machine_max_acceleration_extruding"); + append_option_line(optgroup, "machine_max_acceleration_retracting"); + + optgroup = page->new_optgroup(_(L("Advanced"))); + append_option_line(optgroup, "machine_min_extruding_rate"); + append_option_line(optgroup, "machine_min_travel_rate"); + for (const std::string &axis : axes) { + append_option_line(optgroup, "machine_max_jerk_" + axis); + } + + return page; +} + + +void TabPrinter::build_extruder_pages() +{ size_t n_before_extruders = 2; // Count of pages before Extruder pages + bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin; + + // Add/delete Kinematics page according to is_marlin_flavor + size_t existed_page = 0; + for (int i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already + if (m_pages[i]->title().find(_(L("Machine limits"))) != std::string::npos) { + if (!is_marlin_flavor || m_rebuild_kinematics_page) + m_pages.erase(m_pages.begin() + i); + else + existed_page = i; + break; + } + + if (existed_page < n_before_extruders && is_marlin_flavor){ + auto page = build_kinematics_page(); + m_pages.insert(m_pages.begin() + n_before_extruders, page); + } + + if (is_marlin_flavor) + n_before_extruders++; size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page if (m_extruders_count_old == m_extruders_count || @@ -1796,6 +1898,17 @@ void TabPrinter::update(){ get_field("toolchange_gcode")->toggle(have_multiple_extruders); get_field("single_extruder_multi_material")->toggle(have_multiple_extruders); + bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin; + + const std::string &printer_model = m_config->opt_string("printer_model"); + bool can_use_silent_mode = printer_model.empty() ? false : printer_model == "MK3"; // "true" only for MK3 printers + + get_field("silent_mode")->toggle(can_use_silent_mode && is_marlin_flavor); + if (can_use_silent_mode && m_use_silent_mode != m_config->opt_bool("silent_mode")) { + m_rebuild_kinematics_page = true; + m_use_silent_mode = m_config->opt_bool("silent_mode"); + } + for (size_t i = 0; i < m_extruders_count; ++i) { bool have_retract_length = m_config->opt_float("retract_length", i) > 0; @@ -1914,7 +2027,8 @@ void Tab::rebuild_page_tree() auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); if (p->title() == selected) { - m_disable_tree_sel_changed_event = 1; + if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange + m_disable_tree_sel_changed_event = 1; m_treectrl->SelectItem(itemId); m_disable_tree_sel_changed_event = 0; have_selection = 1; diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp index 0c43f9c97..8b4eae7de 100644 --- a/xs/src/slic3r/GUI/Tab.hpp +++ b/xs/src/slic3r/GUI/Tab.hpp @@ -316,6 +316,9 @@ public: class TabPrinter : public Tab { bool m_has_single_extruder_MM_page = false; + bool m_use_silent_mode = false; + void append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key); + bool m_rebuild_kinematics_page = false; public: wxButton* m_serial_test_btn; wxButton* m_octoprint_host_test_btn; @@ -333,6 +336,7 @@ public: void update() override; void update_serial_ports(); void extruders_count_changed(size_t extruders_count); + PageShp build_kinematics_page(); void build_extruder_pages(); void on_preset_loaded() override; void init_options_list() override; diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index af0612f19..6b05e9a67 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -101,3 +101,6 @@ void desktop_open_datadir_folder() void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst) %code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %}; + +void set_3DScene(SV *scene) + %code%{ Slic3r::GUI::set_3DScene((_3DScene *)wxPli_sv_2_object(aTHX_ scene, "Slic3r::Model::3DScene") ); %}; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index e336131d0..717064916 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -145,8 +145,10 @@ _constant() %code%{ RETVAL = &THIS->skirt; %}; Ref<ExtrusionEntityCollection> brim() %code%{ RETVAL = &THIS->brim; %}; - std::string estimated_print_time() - %code%{ RETVAL = THIS->estimated_print_time; %}; + std::string estimated_normal_print_time() + %code%{ RETVAL = THIS->estimated_normal_print_time; %}; + std::string estimated_silent_print_time() + %code%{ RETVAL = THIS->estimated_silent_print_time; %}; PrintObjectPtrs* objects() %code%{ RETVAL = &THIS->objects; %}; |