From 8f6802fff834194628850163c7059bd1d790bd20 Mon Sep 17 00:00:00 2001 From: HarrierPigeon Date: Mon, 25 May 2026 17:01:13 -0600 Subject: [PATCH] step one: post-process analysis --- src/libslic3r/GCode/GCodeProcessor.cpp | 60 +++++++++++++------ src/libslic3r/GCode/GCodeProcessor.hpp | 7 +++ src/libslic3r/GCode/MachineFrameTransform.cpp | 17 ++++-- src/libslic3r/GCode/MachineFrameTransform.hpp | 10 +++- 4 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ba2ae5db45..bc84963da0 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2765,6 +2765,32 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const int return ps; }; + // Belt-printer post-gcode shear/scale/post_remap is applied as the final + // step of BeltGCodeWriter::to_machine_coords, so MoveVertex.position is + // in the printer's machine frame. Undo it here so XY area and Z height + // checks operate in the build-volume frame that printable_area / + // printable_height are defined in. For non-belt printers + // (is_active() == false) apply_inverse is identity and behaviour is + // unchanged from before. + const bool machine_frame_active = m_machine_frame_transform.is_active(); + auto compare_pos = [&](const GCodeProcessorResult::MoveVertex &move) -> Vec3d { + Vec3d pos = move.position.cast(); + if (!machine_frame_active) + return pos; + Vec3d extruder_off = Vec3d::Zero(); + if (size_t(move.extruder_id) < m_extruder_offsets.size()) + extruder_off = m_extruder_offsets[move.extruder_id].cast(); + // Strip plate + extruder offsets to recover the raw machine-frame + // coordinate that was emitted into the G-code (see store_move_vertex). + Vec3d machine(pos.x() - m_x_offset - extruder_off.x(), + pos.y() - m_y_offset - extruder_off.y(), + pos.z() - extruder_off.z() + m_z_offset); + Vec3d build = m_machine_frame_transform.apply_inverse(machine); + // Re-apply plate offset so the result matches plate_printable_poly, + // which is translated by plate_offset below. + return Vec3d(build.x() + m_x_offset, build.y() + m_y_offset, build.z()); + }; + struct GCodePosInfo { Points pos; @@ -2775,28 +2801,23 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const int std::map> gcode_path_pos; // object_id, filament_id, pos for (const GCodeProcessorResult::MoveVertex &move : m_result.moves) { // sometimes, the start line extrude was outside the edge of plate a little, this is allowed, so do not include into the gcode_path_pos - if (move.type == EMoveType::Extrude /* && move.extrusion_role != ExtrusionRole::erFlush || move.type == EMoveType::Travel*/) + if (move.type == EMoveType::Extrude /* && move.extrusion_role != ExtrusionRole::erFlush || move.type == EMoveType::Travel*/) { + const Vec3d cp = compare_pos(move); + // For belt printers we read Z from the inverse-transformed position + // (post-origin-snap, pre-machine-frame). Otherwise keep the + // original print_z source (the slicer's layer-Z comment) so + // non-belt behaviour is bit-for-bit unchanged. + const float z_for_height = machine_frame_active ? float(cp.z()) : move.print_z; if (move.extrusion_role == ExtrusionRole::erCustom) { - /*if (move.is_arc_move_with_interpolation_points()) { - for (int i = 0; i < move.interpolation_points.size(); i++) { - gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(move.interpolation_points[i].cast())); - } - } else {*/ - gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(move.position.cast())); - //} + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(cp)); gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom = - std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom, move.print_z); + std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom, z_for_height); } else { - /*if (move.is_arc_move_with_interpolation_points()) { - for (int i = 0; i < move.interpolation_points.size(); i++) { - gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.interpolation_points[i].cast())); - } - } else {*/ - gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.position.cast())); - //} + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(cp)); gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z = std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z, - move.print_z); + z_for_height); } + } } bool valid = true; @@ -3036,6 +3057,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_result.printable_height = config.printable_height; + // Belt printer: cache the post-gcode machine-frame transform so the + // multi-extruder validator can undo it and compare against build-volume + // bounds rather than machine-frame positions. + m_machine_frame_transform.init_from_config(config); + auto filament_maps = config.option("filament_map"); if (filament_maps != nullptr) { m_filament_maps = filament_maps->values; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index ce61d25ee4..63f38bf5ef 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -7,6 +7,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/CustomGCode.hpp" #include "libslic3r/MultiNozzleUtils.hpp" +#include "libslic3r/GCode/MachineFrameTransform.hpp" #include #include @@ -1145,6 +1146,12 @@ class Print; double m_x_offset{ 0 }; double m_y_offset{ 0 }; + // Belt-printer post-gcode shear/scale/post_remap. Used by + // check_multi_extruder_gcode_valid to undo the machine-frame + // transform on move positions so bounds checks operate in the + // pre-machine-frame (build-volume) frame. + MachineFrameTransform m_machine_frame_transform; + unsigned int m_line_id; unsigned int m_last_line_id; float m_feedrate; // mm/s diff --git a/src/libslic3r/GCode/MachineFrameTransform.cpp b/src/libslic3r/GCode/MachineFrameTransform.cpp index bdc172d7a4..3d322b4303 100644 --- a/src/libslic3r/GCode/MachineFrameTransform.cpp +++ b/src/libslic3r/GCode/MachineFrameTransform.cpp @@ -7,8 +7,9 @@ namespace Slic3r { bool MachineFrameTransform::init_from_config(const PrintConfig &config) { - m_active = false; - m_transform = Transform3d::Identity(); + m_active = false; + m_transform = Transform3d::Identity(); + m_transform_inverse = Transform3d::Identity(); if (!config.belt_printer.value) return false; @@ -59,8 +60,9 @@ bool MachineFrameTransform::init_from_config(const PrintConfig &config) if (combined.isApprox(Transform3d::Identity())) return false; - m_transform = combined; - m_active = true; + m_transform = combined; + m_transform_inverse = combined.inverse(); + m_active = true; return true; } @@ -71,4 +73,11 @@ Vec3d MachineFrameTransform::apply(const Vec3d &pos) const return m_transform * pos; } +Vec3d MachineFrameTransform::apply_inverse(const Vec3d &pos) const +{ + if (!m_active) + return pos; + return m_transform_inverse * pos; +} + } // namespace Slic3r diff --git a/src/libslic3r/GCode/MachineFrameTransform.hpp b/src/libslic3r/GCode/MachineFrameTransform.hpp index f5e71398da..744a7976c5 100644 --- a/src/libslic3r/GCode/MachineFrameTransform.hpp +++ b/src/libslic3r/GCode/MachineFrameTransform.hpp @@ -31,6 +31,11 @@ public: // Apply the transform to a point. Returns pos unchanged if not active. Vec3d apply(const Vec3d &pos) const; + // Apply the inverse transform. Returns pos unchanged if not active. + // Used by validators that need to compare emitted machine-frame + // coordinates against build-volume bounds. + Vec3d apply_inverse(const Vec3d &pos) const; + bool is_active() const { return m_active; } // The composed shear*scale transform (identity when inactive). Exposed so the @@ -39,8 +44,9 @@ public: const Transform3d& transform() const { return m_transform; } private: - bool m_active = false; - Transform3d m_transform = Transform3d::Identity(); + bool m_active = false; + Transform3d m_transform = Transform3d::Identity(); + Transform3d m_transform_inverse = Transform3d::Identity(); }; } // namespace Slic3r