const prevents calling GCodeGenerator::travel_to functions which change the m_pos internal state.

These methods allow travel moves to be made without checking the distance against XYZ_EPSILON, which may prevent the move from being emitted. This is useful when we want an important comment to be emitted, like a layer change.

get_travel_to_gcode() functions don't maintain m_pos, which later causes incorrect speed calculations as the movement vector is measured incorrectly.

Ensure that client code can't make moves without maintaining m_pos.
This commit is contained in:
jalapnopuzzle 2025-01-26 17:09:30 +11:00 committed by Lukas Matena
parent c83f164ed5
commit 462758153b
5 changed files with 70 additions and 43 deletions

View File

@ -1277,7 +1277,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
file.write(this->retract_and_wipe());
file.write(m_label_objects.maybe_stop_instance());
const double last_z{this->writer().get_position().z()};
file.write(this->writer().get_travel_to_z_gcode(last_z, "ensure z position"));
file.write(this->writer().travel_to_z_force(last_z, "ensure z position"));
const Vec3crd from{to_3d(*this->last_position, scaled(this->m_last_layer_z))};
const Vec3crd to{0, 0, scaled(this->m_last_layer_z)};
file.write(this->travel_to(from, to, ExtrusionRole::None, "move to origin position for next object", [](){return "";}));
@ -2253,7 +2253,7 @@ std::string GCodeGenerator::generate_ramping_layer_change_gcode(
const Polyline &xy_path,
const double initial_elevation,
const GCode::Impl::Travels::ElevatedTravelParams &elevation_params
) const {
) {
using namespace GCode::Impl::Travels;
const std::vector<double> ensure_points_at_distances = linspace(
@ -2271,7 +2271,7 @@ std::string GCodeGenerator::generate_ramping_layer_change_gcode(
for (const Vec3crd &point : travel) {
const Vec3d gcode_point{this->point_to_gcode(point)};
travel_gcode += this->m_writer
.get_travel_to_xyz_gcode(gcode_point, "layer change");
.travel_to_xyz_force(gcode_point, "layer change");
}
return travel_gcode;
}
@ -2992,11 +2992,7 @@ std::string GCodeGenerator::change_layer(
this->last_position = this->gcode_to_point(unscaled(first_point));
} else {
if (!first_layer) {
// travel_to_z is not used as it may not generate the travel if the writter z == print_z.
gcode += this->writer().get_travel_to_z_gcode(print_z, "simple layer change");
Vec3d position{this->writer().get_position()};
position.z() = print_z;
this->writer().update_position(position);
gcode += this->writer().travel_to_z_force(print_z, "simple layer change");
} else {
Vec3d position{this->writer().get_position()};
position.z() = position.z() + m_config.z_offset;
@ -3255,15 +3251,15 @@ std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point, const
if (EXTRUDER_CONFIG(retract_length) > 0 && !this->last_position) {
if (!this->last_position || EXTRUDER_CONFIG(retract_before_travel) < (this->point_to_gcode(*this->last_position) - gcode_point.head<2>()).norm()) {
gcode += this->writer().retract();
gcode += this->writer().get_travel_to_z_gcode(from_z + lift, "lift");
gcode += this->writer().travel_to_z_force(from_z + lift, "lift");
}
}
const std::string comment{"move to first layer point"};
gcode += insert_gcode();
gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment);
gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment);
gcode += this->writer().travel_to_xy_force(gcode_point.head<2>(), comment);
gcode += this->writer().travel_to_z_force(gcode_point.z(), comment);
this->m_avoid_crossing_perimeters.reset_once_modifiers();
this->last_position = point.head<2>();
@ -3319,7 +3315,7 @@ std::string GCodeGenerator::_extrude(
gcode += this->retract_and_wipe();
gcode += m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer);
gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment);
gcode += this->m_writer.get_travel_to_z_gcode(z, comment);
gcode += this->m_writer.travel_to_z_force(z, comment);
} else if ( this->last_position != path.front().point) {
std::string comment = "move to first ";
comment += description;

View File

@ -229,7 +229,7 @@ private:
const Polyline &xy_path,
const double initial_elevation,
const GCode::Impl::Travels::ElevatedTravelParams &elevation_params
) const;
);
std::vector<GCode::ExtrusionOrder::ExtruderExtrusions> get_sorted_extrusions(
const Print &print,

View File

@ -295,19 +295,25 @@ std::string GCodeWriter::set_speed(double F, const std::string_view comment, con
return w.string();
}
std::string GCodeWriter::get_travel_to_xy_gcode(const Vec2d &point, const std::string_view comment) const
std::string GCodeWriter::travel_to_xy_force(const Vec2d &point, const std::string_view comment)
{
GCodeG1Formatter w;
w.emit_xy(point);
w.emit_f(this->config.travel_speed.value * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
m_pos.head<2>() = point.head<2>();
return w.string();
}
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view comment)
{
m_pos.head<2>() = point.head<2>();
return this->get_travel_to_xy_gcode(point, comment);
if (std::abs(point.x() - m_pos.x()) < GCodeFormatter::XYZ_EPSILON
&& std::abs(point.y() - m_pos.y()) < GCodeFormatter::XYZ_EPSILON)
{
return "";
} else {
return this->travel_to_xy_force(point, comment);
}
}
std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment)
@ -329,18 +335,26 @@ std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij
std::string GCodeWriter::travel_to_xyz(const Vec3d &to, const std::string_view comment)
{
if (std::abs(to.x() - m_pos.x()) < GCodeFormatter::XYZ_EPSILON && std::abs(to.y() - m_pos.y()) < GCodeFormatter::XYZ_EPSILON) {
return this->travel_to_z(to.z(), comment);
} else if (std::abs(to.z() - m_pos.z()) < GCodeFormatter::XYZ_EPSILON) {
return this->travel_to_xy(to.head<2>(), comment);
if (std::abs(to.x() - m_pos.x()) < GCodeFormatter::XYZ_EPSILON
&& std::abs(to.y() - m_pos.y()) < GCodeFormatter::XYZ_EPSILON
&& std::abs(to.z() - m_pos.z()) < GCodeFormatter::XYZ_EPSILON)
{
return "";
} else if (
std::abs(to.x() - m_pos.x()) < GCodeFormatter::XYZ_EPSILON
&& std::abs(to.y() - m_pos.y()) < GCodeFormatter::XYZ_EPSILON)
{
return this->travel_to_z_force(to.z(), comment);
} else if (
std::abs(to.z() - m_pos.z()) < GCodeFormatter::XYZ_EPSILON)
{
return this->travel_to_xy_force(to.head<2>(), comment);
} else {
std::string result{this->get_travel_to_xyz_gcode(to, comment)};
m_pos = to;
return result;
return this->travel_to_xyz_force(to, comment);
}
}
std::string GCodeWriter::get_travel_to_xyz_gcode(const Vec3d &to, const std::string_view comment) const {
std::string GCodeWriter::travel_to_xyz_force(const Vec3d &to, const std::string_view comment) {
GCodeG1Formatter w;
w.emit_xyz(to);
@ -360,6 +374,7 @@ std::string GCodeWriter::get_travel_to_xyz_gcode(const Vec3d &to, const std::str
}
w.emit_comment(this->config.gcode_comments, comment);
m_pos = to;
return w.string();
}
@ -368,12 +383,11 @@ std::string GCodeWriter::travel_to_z(double z, const std::string_view comment)
if (std::abs(m_pos.z() - z) < GCodeFormatter::XYZ_EPSILON) {
return "";
} else {
m_pos.z() = z;
return this->get_travel_to_z_gcode(z, comment);
return this->travel_to_z_force(z, comment);
}
}
std::string GCodeWriter::get_travel_to_z_gcode(double z, const std::string_view comment) const
std::string GCodeWriter::travel_to_z_force(double z, const std::string_view comment)
{
double speed = this->config.travel_speed_z.value;
if (speed == 0.)
@ -383,6 +397,7 @@ std::string GCodeWriter::get_travel_to_z_gcode(double z, const std::string_view
w.emit_z(z);
w.emit_f(speed * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
m_pos.z() = z;
return w.string();
}

View File

@ -74,27 +74,46 @@ public:
std::string toolchange(unsigned int extruder_id);
std::string set_speed(double F, const std::string_view comment = {}, const std::string_view cooling_marker = {}) const;
std::string get_travel_to_xy_gcode(const Vec2d &point, const std::string_view comment) const;
std::string travel_to_xy(const Vec2d &point, const std::string_view comment = {});
std::string travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment = {});
/**
* @brief Return gcode with all three axis defined. Optionally adds feedrate.
* @brief Return gcode to travel to the specified point.
* Feed rate is computed based on the vector (to - m_pos).
* Maintains the internal m_pos position.
* Movements less than XYZ_EPSILON generate no output.
*
* Feedrate is added the starting point "from" is specified.
*
* @param from Optional starting point of the travel.
* @param to Where to travel to.
* @param comment Description of the travel purpose.
*/
std::string get_travel_to_xyz_gcode(const Vec3d &to, const std::string_view comment) const;
std::string travel_to_xyz(const Vec3d &to, const std::string_view comment = {});
std::string get_travel_to_z_gcode(double z, const std::string_view comment) const;
std::string travel_to_xy(const Vec2d &point, const std::string_view comment = {});
std::string travel_to_z(double z, const std::string_view comment = {});
std::string travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment = {});
/**
* @brief Generate G-Code to travel to the specified point unconditionally.
* Feed rate is computed based on the vector (to - m_pos).
* Maintains the internal m_pos position.
* The distance test XYZ_EPSILON is not performed.
* @param to The point to travel to.
* @param comment Description of the travel purpose.
*/
std::string travel_to_xyz_force(const Vec3d &to, const std::string_view comment = {});
std::string travel_to_xy_force(const Vec2d &point, const std::string_view comment = {});
std::string travel_to_z_force(double z, const std::string_view comment = {});
/**
* @brief Generate G-Code to move to the specified point while extruding.
* Maintains the internal m_pos position.
* The distance test XYZ_EPSILON is not performed.
* @param point The point to move to.
* @param dE The E-steps to extrude while moving.
* @param comment Description of the movement purpose.
*/
std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {});
std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment = {});
std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment);
// std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment = {});
std::string retract(bool before_wipe = false);
std::string retract_for_toolchange(bool before_wipe = false);
std::string unretract();

View File

@ -78,7 +78,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
);
} else {
gcode += gcodegen.writer().travel_to_xy(gcodegen.point_to_gcode(xy_point), comment);
gcode += gcodegen.writer().get_travel_to_z_gcode(z, comment);
gcode += gcodegen.writer().travel_to_z_force(z, comment);
}
}
gcode += gcodegen.unretract();
@ -100,10 +100,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines.
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z
if (gcodegen.config().wipe_tower) {
deretraction_str += gcodegen.writer().get_travel_to_z_gcode(z, "restore layer Z");
Vec3d position{gcodegen.writer().get_position()};
position.z() = z;
gcodegen.writer().update_position(position);
deretraction_str += gcodegen.writer().travel_to_z_force(z, "restore layer Z");
deretraction_str += gcodegen.unretract();
}
}