PlaceholderParser & G-code export: Exchange of position & extrusion status between custom G-codes and slicer, extension of script syntax.

Newly each custom G-code block may exchange the following values with slicer:

Position and Z-hop:
position (read/write)- 3 element vector (X, Y, Z) of current G-code position. Z element contains the current Z hop.
zhop (read only)- initial zhop value

Extruders: vector variables, one element per extruder:
e_position (read/write) - absolute E position, only available with absolute extruder addressing
e_retracted (read/write) - current retraction state
e_restart_extra (read/write) - current planned extra deretraction when starting printing

For readibility, script's if / elsif / else / endif syntax was modified:

{if cond then
	block
elsif cond then
	block
else
	block
endif}

Semicolon is not required after else or endif.
This commit is contained in:
Vojtech Bubnik 2023-03-28 12:47:23 +02:00
parent f1dd85309b
commit 59552a8aee
6 changed files with 243 additions and 50 deletions

View file

@ -37,6 +37,7 @@ std::pair<double, double> Extruder::extrude(double dE)
value supplied will overwrite the previous one if any. */
std::pair<double, double> 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<double, double> 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
{

View file

@ -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; }

View file

@ -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<Extruder> &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<Extruder> &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<DynamicConfig>();
print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode");
m_placeholder_parser_integration.context.global_config = std::make_unique<DynamicConfig>();
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<unsigned char> 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<double> &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<Extruder&>(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<Extruder&>(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);

View file

@ -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<std::string, std::string> 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<std::string, std::string> 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<double> position;
std::vector<double> e_position;
std::vector<double> e_retracted;
std::vector<double> 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<const PrintObject*, Point> 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.

View file

@ -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;

View file

@ -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);