From e4030321f77b06e8f5f4e1a8c500d8fbbdde546f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 2 Feb 2024 15:05:54 +0100 Subject: [PATCH 1/3] Calculate wipe tower filament consumption per layer --- src/libslic3r/GCode.cpp | 4 ++-- src/libslic3r/GCode/WipeTower.cpp | 9 +++++++-- src/libslic3r/GCode/WipeTower.hpp | 3 ++- src/libslic3r/Print.cpp | 2 +- src/libslic3r/Print.hpp | 4 ++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 04770d1083..6d519d1b18 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -742,8 +742,8 @@ namespace DoExport { print_statistics.printing_extruders.emplace_back(extruder.id()); filament_types.emplace_back(config.filament_type.get_at(extruder.id())); - double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); - double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter + double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament_until_layer.back().second[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament_until_layer.back().second[extruder.id()] * extruder.filament_crossection() : 0.f); // assumes 1.75mm filament diameter double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 06956fd2bc..051828aa45 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1569,8 +1569,9 @@ void WipeTower::generate(std::vector> & } } - for (auto& used : m_used_filament_length) // reset used filament stats - used = 0.f; + m_used_filament_length.assign(m_used_filament_length.size(), 0.f); // reset used filament stats + assert(m_used_filament_length_until_layer.empty()); + m_used_filament_length_until_layer.emplace_back(0.f, m_used_filament_length); m_old_temperature = -1; // reset last temperature written in the gcode @@ -1613,6 +1614,10 @@ void WipeTower::generate(std::vector> & } result.emplace_back(std::move(layer_result)); + + if (m_used_filament_length_until_layer.empty() || m_used_filament_length_until_layer.back().first != layer.z) + m_used_filament_length_until_layer.emplace_back(); + m_used_filament_length_until_layer.back() = std::make_pair(layer.z, m_used_filament_length); } } diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 06919cd4ce..465d783ea6 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -223,7 +223,7 @@ public: return m_current_layer_finished; } - std::vector get_used_filament() const { return m_used_filament_length; } + std::vector>> get_used_filament_until_layer() const { return m_used_filament_length_until_layer; } int get_number_of_toolchanges() const { return m_num_tool_changes; } struct FilamentParameters { @@ -384,6 +384,7 @@ private: // Stores information about used filament length per extruder: std::vector m_used_filament_length; + std::vector>> m_used_filament_length_until_layer; // Return index of first toolchange that switches to non-soluble extruder // ot -1 if there is no such toolchange. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index cd01ee3b6c..6aff4e4c44 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1581,7 +1581,7 @@ void Print::_make_wipe_tower() m_wipe_tower_data.final_purge = Slic3r::make_unique( wipe_tower.tool_change((unsigned int)(-1))); - m_wipe_tower_data.used_filament = wipe_tower.get_used_filament(); + m_wipe_tower_data.used_filament_until_layer = wipe_tower.get_used_filament_until_layer(); m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); m_wipe_tower_data.width = wipe_tower.width(); m_wipe_tower_data.first_layer_height = config().first_layer_height; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1e710272e1..712bb91c22 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -451,7 +451,7 @@ struct WipeTowerData std::unique_ptr> priming; std::vector> tool_changes; std::unique_ptr final_purge; - std::vector used_filament; + std::vector>> used_filament_until_layer; int number_of_toolchanges; // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: @@ -471,7 +471,7 @@ struct WipeTowerData priming.reset(nullptr); tool_changes.clear(); final_purge.reset(nullptr); - used_filament.clear(); + used_filament_until_layer.clear(); number_of_toolchanges = -1; depth = 0.f; z_and_depth_pairs.clear(); From 642535eecbafb8f6a6c9e27366cfa60e7ee812e5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 2 Feb 2024 15:07:23 +0100 Subject: [PATCH 2/3] Added const Print backpointer into GCodeGenerator --- src/libslic3r/GCode.cpp | 35 +++++++++++++++++++++++++++++------ src/libslic3r/GCode.hpp | 33 ++++++++------------------------- src/libslic3r/Print.cpp | 2 +- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6d519d1b18..feeb1423cb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -545,6 +545,29 @@ namespace DoExport { } } // namespace DoExport +GCodeGenerator::GCodeGenerator(const Print* print) : + m_origin(Vec2d::Zero()), + m_enable_loop_clipping(true), + m_enable_cooling_markers(false), + m_enable_extrusion_role_markers(false), + m_last_processor_extrusion_role(GCodeExtrusionRole::None), + m_layer_count(0), + m_layer_index(-1), + m_layer(nullptr), + m_object_layer_over_raft(false), + m_volumetric_speed(0), + m_last_extrusion_role(GCodeExtrusionRole::None), + m_last_width(0.0f), +#if ENABLE_GCODE_VIEWER_DATA_CHECKING + m_last_mm3_per_mm(0.0), +#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + m_brim_done(false), + m_second_layer_things_done(false), + m_silent_time_estimator_enabled(false), + m_current_instance({nullptr, -1}), + m_print(print) + {} + void GCodeGenerator::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) { CNumericLocalesSetter locales_setter; @@ -896,7 +919,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); // config data - encode_full_config(print, binary_data.slicer_metadata.raw_data); + encode_full_config(*m_print, binary_data.slicer_metadata.raw_data); // printer data - this section contains duplicates from the slicer metadata // that we just created. Find and copy the entries that we want to duplicate. @@ -1384,7 +1407,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail { file.write("\n; prusaslicer_config = begin\n"); std::string full_config; - append_full_config(print, full_config); + append_full_config(*m_print, full_config); if (!full_config.empty()) file.write(full_config); file.write("; prusaslicer_config = end\n"); @@ -1740,7 +1763,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc // Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters. // Do not process this piece of G-code by the time estimator, it already knows the values through another sources. -void GCodeGenerator::print_machine_envelope(GCodeOutputStream &file, Print &print) +void GCodeGenerator::print_machine_envelope(GCodeOutputStream &file, const Print &print) { const GCodeFlavor flavor = print.config().gcode_flavor.value; if ( (flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware) @@ -1801,7 +1824,7 @@ void GCodeGenerator::print_machine_envelope(GCodeOutputStream &file, Print &prin // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. // M140 - Set Extruder Temperature // M190 - Set Extruder Temperature and Wait -void GCodeGenerator::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCodeGenerator::_print_first_layer_bed_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { bool autoemit = print.config().autoemit_temperature_commands; // Initial bed temperature based on the first extruder. @@ -1823,7 +1846,7 @@ void GCodeGenerator::_print_first_layer_bed_temperature(GCodeOutputStream &file, // M104 - Set Extruder Temperature // M109 - Set Extruder Temperature and Wait // RepRapFirmware: G10 Sxx -void GCodeGenerator::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCodeGenerator::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { bool autoemit = print.config().autoemit_temperature_commands; // Is the bed temperature set by the provided custom G-code? @@ -2630,7 +2653,7 @@ void GCodeGenerator::apply_print_config(const PrintConfig &print_config) m_scaled_resolution = scaled(print_config.gcode_resolution.value); } -void GCodeGenerator::append_full_config(const Print &print, std::string &str) +void GCodeGenerator::append_full_config(const Print& print, std::string &str) { std::vector> config; encode_full_config(print, config); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 3f2d3685fd..4447bdff0f 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -116,28 +116,8 @@ struct PrintObjectInstance class GCodeGenerator { -public: - GCodeGenerator() : - m_origin(Vec2d::Zero()), - m_enable_loop_clipping(true), - m_enable_cooling_markers(false), - m_enable_extrusion_role_markers(false), - m_last_processor_extrusion_role(GCodeExtrusionRole::None), - m_layer_count(0), - m_layer_index(-1), - m_layer(nullptr), - m_object_layer_over_raft(false), - m_volumetric_speed(0), - m_last_extrusion_role(GCodeExtrusionRole::None), - m_last_width(0.0f), -#if ENABLE_GCODE_VIEWER_DATA_CHECKING - m_last_mm3_per_mm(0.0), -#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING - m_brim_done(false), - m_second_layer_things_done(false), - m_silent_time_estimator_enabled(false), - m_current_instance({nullptr, -1}) - {} +public: + GCodeGenerator(const Print* print = nullptr); // The default value is only used in unit tests. ~GCodeGenerator() = default; // throws std::runtime_exception on error, @@ -468,11 +448,14 @@ private: // Processor GCodeProcessor m_processor; + // Back-pointer to Print (const). + const Print* m_print; + std::string _extrude( const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, const std::string_view description, double speed = -1); - void print_machine_envelope(GCodeOutputStream &file, Print &print); - void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); - void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + void print_machine_envelope(GCodeOutputStream &file, const Print &print); + void _print_first_layer_bed_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); // On the first printing layer. This flag triggers first layer speeds. bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; } // To control print speed of 1st object layer over raft interface. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6aff4e4c44..f69720d6d6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1055,7 +1055,7 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor this->set_status(90, message); // Create GCode on heap, it has quite a lot of data. - std::unique_ptr gcode(new GCodeGenerator); + std::unique_ptr gcode(new GCodeGenerator(const_cast(this))); gcode->do_export(this, path.c_str(), result, thumbnail_cb); if (m_conflict_result.has_value()) From 601d61ca8675dca198e897d5366bd86aedfc2f7c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 2 Feb 2024 17:01:25 +0100 Subject: [PATCH 3/3] Fixed placeholders extruded_volume and similar: wipe tower was not included --- src/libslic3r/GCode.cpp | 18 +++++++++++++++--- src/libslic3r/GCode.hpp | 3 ++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index feeb1423cb..6d619ef167 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -236,7 +236,7 @@ void GCodeGenerator::PlaceholderParserIntegration::init(const GCodeWriter &write this->parser.set("zhop", this->opt_zhop); } -void GCodeGenerator::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) +void GCodeGenerator::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer, const WipeTowerData& wipe_tower_data) { memcpy(this->position.data(), writer.get_position().data(), sizeof(double) * 3); this->opt_position->values = this->position; @@ -253,7 +253,19 @@ void GCodeGenerator::PlaceholderParserIntegration::update_from_gcodewriter(const for (const Extruder &e : extruders) { this->e_retracted[e.id()] = e.retracted(); this->e_restart_extra[e.id()] = e.restart_extra(); - double v = e.extruded_volume(); + + // Wipe tower filament consumption has to be added separately, because that gcode is not generated by GCodeWriter. + double wt_vol = 0.; + const std::vector>>& wtuf = wipe_tower_data.used_filament_until_layer; + if (!wtuf.empty()) { + auto it = std::lower_bound(wtuf.begin(), wtuf.end(), writer.get_position().z(), + [](const auto& a, const float& val) { return a.first < val; }); + if (it == wtuf.end()) + it = wtuf.end() - 1; + wt_vol = it->second[e.id()] * e.filament_crossection(); + } + + double v = e.extruded_volume() + wt_vol; double w = v * e.filament_density() * 0.001; this->opt_extruded_volume->values[e.id()] = v; this->opt_extruded_weight->values[e.id()] = w; @@ -1657,7 +1669,7 @@ std::string GCodeGenerator::placeholder_parser_process( PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { - ppi.update_from_gcodewriter(m_writer); + ppi.update_from_gcodewriter(m_writer, m_print->wipe_tower_data()); std::string output = ppi.parser.process(templ, current_extruder_id, config_override, &ppi.output_config, &ppi.context); ppi.validate_output_vector_variables(); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 4447bdff0f..4d18835332 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -53,6 +53,7 @@ namespace Slic3r { // Forward declarations. class GCodeGenerator; +struct WipeTowerData; namespace { struct Item; } struct PrintInstance; @@ -356,7 +357,7 @@ private: struct PlaceholderParserIntegration { void reset(); void init(const GCodeWriter &config); - void update_from_gcodewriter(const GCodeWriter &writer); + void update_from_gcodewriter(const GCodeWriter &writer, const WipeTowerData& wipe_tower_data); void validate_output_vector_variables(); PlaceholderParser parser;