From c1a90fc451df1c18bd5fef9000e518e9f2c259f3 Mon Sep 17 00:00:00 2001 From: harrierpigeon Date: Wed, 5 Aug 2026 03:39:47 -0500 Subject: [PATCH] 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. --- src/libslic3r/BeltGCodeWriter.cpp | 4 ++- src/libslic3r/GCode.cpp | 14 +++++----- src/libslic3r/GCodeWriter.cpp | 45 +++++++++++++++++++++++++------ 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/BeltGCodeWriter.cpp b/src/libslic3r/BeltGCodeWriter.cpp index e1dad4177f..4824d76f69 100644 --- a/src/libslic3r/BeltGCodeWriter.cpp +++ b/src/libslic3r/BeltGCodeWriter.cpp @@ -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; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 5b76f53ab5..46d8593182 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -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 diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 491317e567..f3566b0a4b 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -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