diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index d2ff650974..b1a089d085 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -37,6 +37,7 @@ std::pair Extruder::extrude(double dE) value supplied will overwrite the previous one if any. */ std::pair Extruder::retract(double retract_length, double restart_extra) { + assert(restart_extra >= 0); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; @@ -64,6 +65,24 @@ std::pair Extruder::unretract() return std::make_pair(dE, emitE); } +// Setting the retract state from the script. +// Sets current retraction value & restart extra filament amount if retracted > 0. +void Extruder::set_retracted(double retracted, double restart_extra) +{ + if (retracted < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative z_retracted."); + if (restart_extra < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative z_restart_extra."); + + if (retracted > EPSILON) { + m_retracted = retracted; + m_restart_extra = restart_extra < EPSILON ? 0 : restart_extra; + } else { + m_retracted = 0; + m_restart_extra = 0; + } +} + // Used filament volume in mm^3. double Extruder::extruded_volume() const { diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp index 7491b1c8f5..7c37f1934c 100644 --- a/src/libslic3r/Extruder.hpp +++ b/src/libslic3r/Extruder.hpp @@ -36,6 +36,19 @@ public: double extruded_volume() const; // Used filament length in mm. double used_filament() const; + + // Getters for the PlaceholderParser. + // Get current extruder position. Only applicable with absolute extruder addressing. + double position() const { return m_E; } + // Get current retraction value. Only non-negative values. + double retracted() const { return m_retracted; } + // Get extra retraction planned after + double restart_extra() const { return m_restart_extra; } + // Setters for the PlaceholderParser. + // Set current extruder position. Only applicable with absolute extruder addressing. + void set_position(double e) { m_E = e; } + // Sets current retraction value & restart extra filament amount if retracted > 0. + void set_retracted(double retracted, double restart_extra); double filament_diameter() const; double filament_crossection() const { return this->filament_diameter() * this->filament_diameter() * 0.25 * PI; } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d4278ed749..22c26b3a55 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -419,7 +419,7 @@ namespace Slic3r { std::string WipeTowerIntegration::finalize(GCode& gcodegen) { std::string gcode; - if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) + if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) gcode += gcodegen.change_layer(m_final_purge.print_z); gcode += append_tcr(gcodegen, m_final_purge, -1); return gcode; @@ -429,6 +429,90 @@ namespace Slic3r { #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) +void GCode::PlaceholderParserIntegration::reset() +{ + this->failed_templates.clear(); + this->output_config.clear(); + this->opt_position = nullptr; + this->opt_zhop = nullptr; + this->opt_e_position = nullptr; + this->opt_e_retracted = nullptr; + this->opt_e_restart_extra = nullptr; + this->num_extruders = 0; + this->position.clear(); + this->e_position.clear(); + this->e_retracted.clear(); + this->e_restart_extra.clear(); +} + +void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) +{ + this->reset(); + const std::vector &extruders = writer.extruders(); + if (! extruders.empty()) { + this->num_extruders = extruders.back().id() + 1; + this->e_retracted.assign(num_extruders, 0); + this->e_restart_extra.assign(num_extruders, 0); + this->opt_e_retracted = new ConfigOptionFloats(e_retracted); + this->opt_e_restart_extra = new ConfigOptionFloats(e_restart_extra); + this->output_config.set_key_value("e_retracted", this->opt_e_retracted); + this->output_config.set_key_value("e_restart_extra", this->opt_e_restart_extra); + if (! writer.config.use_relative_e_distances) { + e_position.assign(num_extruders, 0); + opt_e_position = new ConfigOptionFloats(e_position); + this->output_config.set_key_value("e_position", opt_e_position); + } + } + + // Reserve buffer for current position. + this->position.assign(3, 0); + this->opt_position = new ConfigOptionFloats(this->position); + // Store zhop variable into the parser itself, it is a read-only variable to the script. + this->opt_zhop = new ConfigOptionFloat(writer.get_zhop()); + this->parser.set("zhop", this->opt_zhop); +} + +void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) +{ + memcpy(this->position.data(), writer.get_position().data(), sizeof(double) * 3); + this->opt_position->values = this->position; + this->opt_zhop->value = writer.get_zhop(); + + if (this->num_extruders > 0) { + const std::vector &extruders = writer.extruders(); + assert(! extruders.empty() && num_extruders == extruders.back().id() + 1); + this->e_retracted.assign(num_extruders, 0); + this->e_restart_extra.assign(num_extruders, 0); + for (const Extruder &e : extruders) { + this->e_retracted[e.id()] = e.retracted(); + this->e_restart_extra[e.id()] = e.restart_extra(); + } + opt_e_retracted->values = this->e_retracted; + opt_e_restart_extra->values = this->e_restart_extra; + if (! writer.config.use_relative_e_distances) { + this->e_position.assign(num_extruders, 0); + for (const Extruder &e : extruders) + this->e_position[e.id()] = e.position(); + this->opt_e_position->values = this->e_position; + } + } +} + +// Throw if any of the output vector variables were resized by the script. +void GCode::PlaceholderParserIntegration::validate_output_vector_variables() +{ + if (this->opt_position->values.size() != 3) + throw Slic3r::RuntimeError("\"position\" output variable must not be resized by the script."); + if (this->num_extruders > 0) { + if (this->opt_e_position && this->opt_e_position->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_position\" output variable must not be resized by the script."); + if (this->opt_e_retracted->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_retracted\" output variable must not be resized by the script."); + if (this->opt_e_restart_extra->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_restart_extra\" output variable must not be resized by the script."); + } +} + // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& object) @@ -728,7 +812,6 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); try { - m_placeholder_parser_failed_templates.clear(); this->_do_export(*print, file, thumbnail_cb); file.flush(); if (file.is_error()) { @@ -745,11 +828,11 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu } file.close(); - if (! m_placeholder_parser_failed_templates.empty()) { + if (! m_placeholder_parser_integration.failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. //FIXME localize! std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; - for (const auto &name_and_error : m_placeholder_parser_failed_templates) + for (const auto &name_and_error : m_placeholder_parser_integration.failed_templates) msg += name_and_error.first + "\n" + name_and_error.second + "\n"; msg += "\nPlease inspect the file "; msg += path_tmp + " for error messages enclosed between\n"; @@ -1078,12 +1161,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.find_replace_enable(); // Prepare the helper object for replacing placeholders in custom G-code and output filename. - m_placeholder_parser = print.placeholder_parser(); - m_placeholder_parser.update_timestamp(); - m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + m_placeholder_parser_integration.parser = print.placeholder_parser(); + m_placeholder_parser_integration.parser.update_timestamp(); + m_placeholder_parser_integration.context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); // Enable passing global variables between PlaceholderParser invocations. - m_placeholder_parser_context.global_config = std::make_unique(); - print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode"); + m_placeholder_parser_integration.context.global_config = std::make_unique(); + print.update_object_placeholders(m_placeholder_parser_integration.parser.config_writable(), ".gcode"); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. // For a print by objects, find the 1st printing object. @@ -1147,23 +1230,25 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Emit machine envelope limits for the Marlin firmware. this->print_machine_envelope(file, print); + // Update output variables after the extruders were initialized. + m_placeholder_parser_integration.init(m_writer); // Let the start-up script prime the 1st printing tool. - m_placeholder_parser.set("initial_tool", initial_extruder_id); - m_placeholder_parser.set("initial_extruder", initial_extruder_id); - m_placeholder_parser.set("current_extruder", initial_extruder_id); + this->placeholder_parser().set("initial_tool", initial_extruder_id); + this->placeholder_parser().set("initial_extruder", initial_extruder_id); + this->placeholder_parser().set("current_extruder", initial_extruder_id); //Set variable for total layer count so it can be used in custom gcode. - m_placeholder_parser.set("total_layer_count", m_layer_count); + this->placeholder_parser().set("total_layer_count", m_layer_count); // Useful for sequential prints. - m_placeholder_parser.set("current_object_idx", 0); + this->placeholder_parser().set("current_object_idx", 0); // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. - m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); - m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); - m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). + this->placeholder_parser().set("has_wipe_tower", has_wipe_tower); + this->placeholder_parser().set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); + this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). { BoundingBoxf bbox(print.config().bed_shape.values); - m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); - m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); - m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); } { // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. @@ -1176,15 +1261,15 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato for (const Point &pt : print.first_layer_convex_hull().points) pts->values.emplace_back(unscale(pt)); BoundingBoxf bbox(pts->values); - m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); - m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); - m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); - m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + this->placeholder_parser().set("first_layer_print_convex_hull", pts.release()); + this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); std::vector is_extruder_used(print.config().nozzle_diameter.size(), 0); for (unsigned int extruder_id : tool_ordering.all_extruders()) is_extruder_used[extruder_id] = true; - m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used)); + this->placeholder_parser().set("is_extruder_used", new ConfigOptionBools(is_extruder_used)); } // Enable ooze prevention if configured so. @@ -1251,7 +1336,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. - m_placeholder_parser.set("current_object_idx", int(finished_objects)); + this->placeholder_parser().set("current_object_idx", int(finished_objects)); std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); @@ -1337,7 +1422,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); if (print.config().single_extruder_multi_material) { // Process the end_filament_gcode for the active filament only. @@ -1560,17 +1645,46 @@ void GCode::process_layers( output_stream.find_replace_enable(); } -std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) +std::string GCode::placeholder_parser_process( + const std::string &name, + const std::string &templ, + unsigned int current_extruder_id, + const DynamicConfig *config_override) { + PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { - return m_placeholder_parser.process(templ, current_extruder_id, config_override, &m_placeholder_parser_context); - } catch (std::runtime_error &err) { + ppi.update_from_gcodewriter(m_writer); + std::string output = ppi.parser.process(templ, current_extruder_id, config_override, &ppi.output_config, &ppi.context); + ppi.validate_output_vector_variables(); + + if (const std::vector &pos = ppi.opt_position->values; ppi.position != pos) { + // Update G-code writer. + m_writer.update_position({ pos[0], pos[1], pos[2] }); + this->set_last_pos(this->gcode_to_point({ pos[0], pos[1] })); + } + + for (const Extruder &e : m_writer.extruders()) { + unsigned int eid = e.id(); + assert(eid < ppi.num_extruders); + if ( eid < ppi.num_extruders) { + if (! m_writer.config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid])) + const_cast(e).set_position(ppi.opt_e_position->values[eid]); + if (! is_approx(ppi.e_retracted[eid], ppi.opt_e_retracted->values[eid]) || + ! is_approx(ppi.e_restart_extra[eid], ppi.opt_e_restart_extra->values[eid])) + const_cast(e).set_retracted(ppi.opt_e_retracted->values[eid], ppi.opt_e_restart_extra->values[eid]); + } + } + + return output; + } + catch (std::runtime_error &err) + { // Collect the names of failed template substitutions for error reporting. - auto it = m_placeholder_parser_failed_templates.find(name); - if (it == m_placeholder_parser_failed_templates.end()) + auto it = ppi.failed_templates.find(name); + if (it == ppi.failed_templates.end()) // Only if there was no error reported for this template, store the first error message into the map to be reported. // We don't want to collect error message for each and every occurence of a single custom G-code section. - m_placeholder_parser_failed_templates.insert(it, std::make_pair(name, std::string(err.what()))); + ppi.failed_templates.insert(it, std::make_pair(name, std::string(err.what()))); // Insert the macro error message into the G-code. return std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" + @@ -3160,7 +3274,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // if we are running a single-extruder setup, just set the extruder and return nothing if (!m_writer.multiple_extruders) { - m_placeholder_parser.set("current_extruder", extruder_id); + this->placeholder_parser().set("current_extruder", extruder_id); std::string gcode; // Append the filament start G-code. @@ -3169,7 +3283,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Process the start_filament_gcode for the filament. DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); @@ -3233,7 +3347,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) gcode += m_writer.set_temperature(temp, false); } - m_placeholder_parser.set("current_extruder", extruder_id); + this->placeholder_parser().set("current_extruder", extruder_id); // Append the filament start G-code. const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); @@ -3241,7 +3355,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Process the start_filament_gcode for the new filament. DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index ee50aefccc..98f49095dc 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -173,8 +173,8 @@ public: const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } const GCodeWriter& writer() const { return m_writer; } - PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } - const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } + PlaceholderParser& placeholder_parser() { return m_placeholder_parser_integration.parser; } + const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser_integration.parser; } // Process a template through the placeholder parser, collect error messages to be reported // inside the generated string and after the G-code export finishes. std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr); @@ -343,11 +343,33 @@ private: // scaled G-code resolution double m_scaled_resolution; GCodeWriter m_writer; - PlaceholderParser m_placeholder_parser; - // For random number generator etc. - PlaceholderParser::ContextData m_placeholder_parser_context; - // Collection of templates, on which the placeholder substitution failed. - std::map m_placeholder_parser_failed_templates; + + struct PlaceholderParserIntegration { + void reset(); + void init(const GCodeWriter &config); + void update_from_gcodewriter(const GCodeWriter &writer); + void validate_output_vector_variables(); + + PlaceholderParser parser; + // For random number generator etc. + PlaceholderParser::ContextData context; + // Collection of templates, on which the placeholder substitution failed. + std::map failed_templates; + // Input/output from/to custom G-code block, for returning position, retraction etc. + DynamicConfig output_config; + ConfigOptionFloats *opt_position { nullptr }; + ConfigOptionFloat *opt_zhop { nullptr }; + ConfigOptionFloats *opt_e_position { nullptr }; + ConfigOptionFloats *opt_e_retracted { nullptr }; + ConfigOptionFloats *opt_e_restart_extra { nullptr }; + // Caches of the data passed to the script. + size_t num_extruders; + std::vector position; + std::vector e_position; + std::vector e_retracted; + std::vector e_restart_extra; + } m_placeholder_parser_integration; + OozePrevention m_ooze_prevention; Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; @@ -403,15 +425,15 @@ private: // Index of a last object copy extruded. std::pair m_last_obj_copy; - bool m_silent_time_estimator_enabled; + bool m_silent_time_estimator_enabled; // Processor - GCodeProcessor m_processor; + GCodeProcessor m_processor; - std::string _extrude(const ExtrusionPath &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); + std::string _extrude(const ExtrusionPath &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); // 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/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 5080fabb4c..9c330c38e9 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -486,6 +486,20 @@ std::string GCodeWriter::unlift() return gcode; } +void GCodeWriter::update_position(const Vec3d &new_pos) +{ + assert(this->m_lifted >= 0); + const double nominal_z = m_pos.z() - m_lifted; + m_lifted = new_pos.z() - nominal_z; + if (m_lifted < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative Z-hop. Final Z position is below the print_z height."); + // In case that retract_lift == layer_height we could end up with almost zero in_m_lifted + // and a retract could be skipped (https://github.com/prusa3d/PrusaSlicer/issues/2154 + if (m_lifted < EPSILON) + m_lifted = 0.; + m_pos = new_pos; +} + std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed) { std::ostringstream gcode; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 9e5fce7023..0d376cb159 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -68,7 +68,18 @@ public: std::string unretract(); std::string lift(); std::string unlift(); + + // Current position of the printer, in G-code coordinates. + // Z coordinate of current position contains zhop. If zhop is applied (this->zhop() > 0), + // then the print_z = this->get_position().z() - this->zhop(). Vec3d get_position() const { return m_pos; } + // Current Z hop value. + double get_zhop() const { return m_lifted; } + // Update position of the print head based on the final position returned by a custom G-code block. + // The new position Z coordinate contains the Z-hop. + // GCodeWriter expects the custom script to NOT change print_z, only Z-hop, thus the print_z is maintained + // by this function while the current Z-hop accumulator is updated. + void update_position(const Vec3d &new_pos); // Returns whether this flavor supports separate print and travel acceleration. static bool supports_separate_travel_acceleration(GCodeFlavor flavor);