Fix: correct axis-remap G-code emission and belt first-layer travel speed (B2, B3)

- BeltGCodeWriter::travel_to_xyz final branch used config.travel_speed
  instead of the computed first-layer-aware travel_speed.
- extrude_to_xyz decided emit_xyz vs emit_xy from pre-remap Z; emit full
  XYZ whenever an axis remap is active so remapped machine-Z is never
  dropped.
- base travel_to_xyz now applies apply_axis_remap() on all emitted
  destinations (standalone remap on non-belt printers was unremapped).
- spiral/arc travels fall back to normal linear lift under active remap
  (endpoint-only remap can't preserve arc plane/I-J).
- set_axis_remap() is now synced unconditionally each export to avoid a
  reused writer retaining a stale non-identity mapping.
This commit is contained in:
harrierpigeon
2026-08-05 03:39:47 -05:00
parent 7fc86db5f0
commit c1a90fc451
3 changed files with 48 additions and 15 deletions

View File

@@ -264,7 +264,9 @@ std::string BeltGCodeWriter::travel_to_xyz(const Vec3d &point, const std::string
// Belt mode: always emit full XYZ
GCodeG1Formatter w;
w.emit_xyz(point_on_plate);
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
// Use the first-layer-aware travel_speed computed at the top of this function,
// not the raw config travel_speed, so initial-layer travels are correctly slowed.
w.emit_f(travel_speed * 60.0);
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
m_pos = dest_point;

View File

@@ -2952,16 +2952,18 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->init_belt_writer(print, is_bbl_printers);
// Standalone axis remap (works with or without belt mode).
// Sync the writer's remap state to the current export UNCONDITIONALLY — even at
// the identity mapping (0,1,2) — so a reused writer never retains a stale
// non-identity mapping from a prior export. has_axis_remap() returns false at
// identity, so identity/default output stays unchanged.
{
int rx = int(print.config().gcode_remap_x.value);
int ry = int(print.config().gcode_remap_y.value);
int rz = int(print.config().gcode_remap_z.value);
if (rx != 0 || ry != 1 || rz != 2) {
m_writer->set_axis_remap(rx, ry, rz);
BoundingBoxf bbox_bed(print.config().printable_area.values);
m_writer->set_build_volume_max(Vec3d(bbox_bed.max.x(), bbox_bed.max.y(),
print.config().printable_height.value));
}
m_writer->set_axis_remap(rx, ry, rz);
BoundingBoxf bbox_bed(print.config().printable_area.values);
m_writer->set_build_volume_max(Vec3d(bbox_bed.max.x(), bbox_bed.max.y(),
print.config().printable_height.value));
}
// Build the FirstLayerPlane evaluator. When inactive (non-belt printers

View File

@@ -936,7 +936,10 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
Vec2d temp = delta_no_z.normalized() * delta(2) / tan(this->filament()->travel_slope());
Vec3d slope_top_point = Vec3d(temp(0), temp(1), delta(2)) + source;
GCodeG1Formatter w0;
w0.emit_xyz(slope_top_point);
// A slope lift is a straight (linear) diagonal move, so remapping its
// endpoint is exact. Route the destination through apply_axis_remap()
// when a remap is active (no-op at identity).
w0.emit_xyz(has_axis_remap() ? apply_axis_remap(slope_top_point) : slope_top_point);
w0.emit_f(travel_speed * 60.0);
//BBS
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
@@ -950,7 +953,14 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
std::string xy_z_move;
{
GCodeG1Formatter w0;
if (this->is_current_position_clear()) {
if (has_axis_remap()) {
// Remap may couple XY with Z; emit full XYZ in machine coordinates.
w0.emit_xyz(apply_axis_remap(target));
w0.emit_f(travel_speed * 60.0);
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
xy_z_move = w0.string();
}
else if (this->is_current_position_clear()) {
w0.emit_xyz(target);
w0.emit_f(travel_speed * 60.0);
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
@@ -988,7 +998,13 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
Vec3d point_on_plate = { dest_point(0) - m_x_offset, dest_point(1) - m_y_offset, dest_point(2) };
std::string out_string;
GCodeG1Formatter w;
if (!this->is_current_position_clear())
if (has_axis_remap()) {
// Remap may couple XY with Z; emit full XYZ in machine coordinates.
w.emit_xyz(apply_axis_remap(point_on_plate));
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
out_string = w.string();
} else if (!this->is_current_position_clear())
{
//force to move xy first then z after filament change
w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y()));
@@ -1053,6 +1069,14 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
std::string GCodeWriter::_spiral_travel_to_z(double z, const Vec2d &ij_offset, const std::string &comment)
{
// A circular XY arc / spiral lift cannot be correctly axis-remapped by
// transforming only its endpoint: the arc plane (G17/XY) and the I-J center
// would change under the remap. When an axis remap is active, fall back to a
// plain linear lift instead of emitting a possibly-wrong spiral/arc. This
// single guard covers every spiral call site (lazy/eager lift and travel_to_xyz).
if (has_axis_remap())
return _travel_to_z(z, comment);
std::string output;
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
@@ -1199,14 +1223,19 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
//BBS: take plate offset into consider
Vec3d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset, point(2) };
if (has_axis_remap())
point_on_plate = apply_axis_remap(point_on_plate);
GCodeG1Formatter w;
if (z_changed)
if (has_axis_remap()) {
// z_changed was computed from the ORIGINAL slicing Z, but an axis remap can
// make machine-Z depend on slicing X/Y. An X/Y-only move (slicing-Z
// unchanged) would then drop the required machine-Z word, so always emit
// full XYZ whenever a remap is active.
point_on_plate = apply_axis_remap(point_on_plate);
w.emit_xyz(point_on_plate);
else
} else if (z_changed) {
w.emit_xyz(point_on_plate);
} else {
w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y()));
}
if (!force_no_extrusion)
w.emit_e(filament()->E());
//BBS