From c8a1bf3a99f61ec972d0691e2ad46077ce415945 Mon Sep 17 00:00:00 2001 From: harrierpigeon Date: Sat, 28 Mar 2026 02:13:15 -0500 Subject: [PATCH] Part 3.2: decouple axis remapping, enable viewing settings in Developer mode or when Belt mode is active --- src/libslic3r/BeltGCode.cpp | 14 ++---- src/libslic3r/BeltGCodeWriter.cpp | 25 ++-------- src/libslic3r/BeltGCodeWriter.hpp | 8 +--- src/libslic3r/BeltSliceStrategy.cpp | 15 ++---- src/libslic3r/BeltSliceStrategy.hpp | 10 ++-- src/libslic3r/BeltTransform.cpp | 18 ++++---- src/libslic3r/BeltTransform.hpp | 14 +++--- src/libslic3r/GCode.cpp | 13 ++++++ src/libslic3r/GCode/BeltBackTransform.cpp | 2 +- src/libslic3r/GCode/BeltBackTransform.hpp | 2 +- src/libslic3r/GCode/GCodeProcessor.cpp | 34 +++++++------- src/libslic3r/GCode/GCodeProcessor.hpp | 12 ++--- src/libslic3r/GCodeWriter.cpp | 56 +++++++++++++++++++++-- src/libslic3r/GCodeWriter.hpp | 15 ++++++ src/libslic3r/Preset.cpp | 4 +- src/libslic3r/Print.cpp | 12 ++--- src/libslic3r/PrintConfig.cpp | 52 ++++++++++----------- src/libslic3r/PrintConfig.hpp | 18 ++++---- src/libslic3r/PrintObjectSlice.cpp | 24 +++++++++- src/slic3r/GUI/Tab.cpp | 21 +++++---- 20 files changed, 215 insertions(+), 154 deletions(-) diff --git a/src/libslic3r/BeltGCode.cpp b/src/libslic3r/BeltGCode.cpp index dc1b6ca439..40dabf98dc 100644 --- a/src/libslic3r/BeltGCode.cpp +++ b/src/libslic3r/BeltGCode.cpp @@ -15,13 +15,7 @@ void BeltGCode::init_belt_writer(Print &print, bool is_bbl_printers) auto belt_writer = std::make_unique(); belt_writer->set_is_bbl_machine(is_bbl_printers); belt_writer->set_belt_angle(print.config().belt_printer_angle.value); - belt_writer->set_axis_remap( - int(print.config().belt_gcode_remap_x.value), - int(print.config().belt_gcode_remap_y.value), - int(print.config().belt_gcode_remap_z.value)); - BoundingBoxf bbox_bed(print.config().printable_area.values); - belt_writer->set_build_volume_max(Vec3d(bbox_bed.max.x(), bbox_bed.max.y(), - print.config().printable_height.value)); + // Axis remap and build volume max are set by base GCode after init_belt_writer returns. belt_writer->set_belt_back_transform(print.config()); m_writer = std::move(belt_writer); @@ -59,9 +53,9 @@ void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print) file.write_format("; belt_scale_z = %s\n", full_cfg.opt_serialize("belt_scale_z").c_str()); file.write_format("; belt_scale_z_angle = %.1f\n", print.config().belt_scale_z_angle.value); // Pre-slice remap configs - file.write_format("; belt_preslice_remap_x = %s\n", full_cfg.opt_serialize("belt_preslice_remap_x").c_str()); - file.write_format("; belt_preslice_remap_y = %s\n", full_cfg.opt_serialize("belt_preslice_remap_y").c_str()); - file.write_format("; belt_preslice_remap_z = %s\n", full_cfg.opt_serialize("belt_preslice_remap_z").c_str()); + file.write_format("; preslice_remap_x = %s\n", full_cfg.opt_serialize("preslice_remap_x").c_str()); + file.write_format("; preslice_remap_y = %s\n", full_cfg.opt_serialize("preslice_remap_y").c_str()); + file.write_format("; preslice_remap_z = %s\n", full_cfg.opt_serialize("preslice_remap_z").c_str()); } void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift) diff --git a/src/libslic3r/BeltGCodeWriter.cpp b/src/libslic3r/BeltGCodeWriter.cpp index 645bf85a5b..db2bc46fe1 100644 --- a/src/libslic3r/BeltGCodeWriter.cpp +++ b/src/libslic3r/BeltGCodeWriter.cpp @@ -10,18 +10,6 @@ void BeltGCodeWriter::set_belt_angle(double angle_deg) m_belt_angle_rad = Geometry::deg2rad(angle_deg); } -void BeltGCodeWriter::set_axis_remap(int rx, int ry, int rz) -{ - m_remap_x = rx; - m_remap_y = ry; - m_remap_z = rz; -} - -void BeltGCodeWriter::set_build_volume_max(const Vec3d &max) -{ - m_build_vol_max = max; -} - void BeltGCodeWriter::set_belt_back_transform(const PrintConfig &config) { m_belt_back_transform.init_from_config(config); @@ -40,16 +28,9 @@ Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const { // Step 1: Undo the shear/scale applied during slicing. Vec3d p = m_belt_back_transform.apply(pos); - // Step 2: Apply axis remapping for the machine's coordinate convention. - // BeltRemapAxis: 0-2 = +X/+Y/+Z, 3-5 = -X/-Y/-Z, 6-8 = Rev X/Y/Z - auto remap = [this, &p](int r) -> double { - int axis = r % 3; - if (r < 3) return p[axis]; - if (r < 6) return -p[axis]; - return m_build_vol_max[axis] - p[axis]; - }; - Vec3d result = { remap(m_remap_x), remap(m_remap_y), remap(m_remap_z) }; - // Per-axis origin snap: shift so bbox min on each enabled axis = offset. + // Step 2: Apply axis remap (uses inherited base class method). + Vec3d result = apply_axis_remap(p); + // Step 3: Per-axis origin snap. for (int i = 0; i < 3; ++i) if (m_origin_snap[i]) result[i] -= (m_origin_bbox_min[i] - m_origin_offset[i]); diff --git a/src/libslic3r/BeltGCodeWriter.hpp b/src/libslic3r/BeltGCodeWriter.hpp index 727135b578..a41f890503 100644 --- a/src/libslic3r/BeltGCodeWriter.hpp +++ b/src/libslic3r/BeltGCodeWriter.hpp @@ -15,11 +15,9 @@ class BeltGCodeWriter : public GCodeWriter public: BeltGCodeWriter() : GCodeWriter() {} - // Belt configuration + // Belt configuration (axis remap is inherited from GCodeWriter) void set_belt_angle(double angle_deg); bool is_belt_printer() const { return m_belt_angle_rad != 0.; } - void set_axis_remap(int rx, int ry, int rz); - void set_build_volume_max(const Vec3d &max); void set_belt_back_transform(const PrintConfig &config); void set_origin_snap(int axis, bool enable, double offset, double bbox_min); Vec3d to_machine_coords(const Vec3d &pos) const; @@ -37,10 +35,6 @@ protected: private: double m_belt_angle_rad = 0.; - int m_remap_x = 0; - int m_remap_y = 1; - int m_remap_z = 2; - Vec3d m_build_vol_max = Vec3d::Zero(); BeltBackTransform m_belt_back_transform; bool m_origin_snap[3] = {false, false, false}; double m_origin_offset[3] = {0., 0., 0.}; diff --git a/src/libslic3r/BeltSliceStrategy.cpp b/src/libslic3r/BeltSliceStrategy.cpp index b3adeec64a..c1a300869e 100644 --- a/src/libslic3r/BeltSliceStrategy.cpp +++ b/src/libslic3r/BeltSliceStrategy.cpp @@ -13,31 +13,24 @@ std::unique_ptr BeltSliceStrategy::create(const PrintConfig & BeltSliceStrategy::BeltSliceStrategy(const PrintConfig &config) { - m_has_remap = BeltTransformPipeline::has_preslice_remap(config); - if (m_has_remap) - m_pre_remap = BeltTransformPipeline::build_preslice_remap(config); - m_shear = BeltTransformPipeline::build_shear_matrix(config, &m_has_shear); m_scale = BeltTransformPipeline::build_scale_matrix(config, &m_has_scale); } void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo, const ModelVolumePtrs &model_volumes, + bool has_remap, double *out_belt_min_z) const { - // Step 1: Pre-slice axis remap. - if (m_has_remap) - trafo = m_pre_remap * trafo; - - // Step 2: Shear + scale. + // Shear + scale (belt-only; pre-slice remap is handled separately). if (m_has_shear || m_has_scale) { Transform3d belt_xform = Transform3d::Identity(); belt_xform.linear() = m_scale * m_shear; trafo = belt_xform * trafo; } - // Step 3: Z-shift — detect if mesh clips below build plate after transforms. - if (m_has_remap || m_has_shear || m_has_scale) { + // Z-shift — detect if mesh clips below build plate after transforms. + if (has_remap || m_has_shear || m_has_scale) { double min_z = std::numeric_limits::max(); for (const ModelVolume *mv : model_volumes) { if (!mv->is_model_part()) continue; diff --git a/src/libslic3r/BeltSliceStrategy.hpp b/src/libslic3r/BeltSliceStrategy.hpp index bf19243fd2..ef6e038ee7 100644 --- a/src/libslic3r/BeltSliceStrategy.hpp +++ b/src/libslic3r/BeltSliceStrategy.hpp @@ -23,21 +23,19 @@ public: // Create a strategy if belt_printer is enabled; returns nullptr otherwise. static std::unique_ptr create(const PrintConfig &config); - // Apply belt transforms to the slicing trafo. - // Modifies trafo in-place: trafo = z_shift * scale * shear * pre_remap * trafo - // Scans all model_part volumes to detect minimum Z and add z-shift if needed. - // Sets *out_belt_min_z to the minimum Z of the mesh after transforms. + // Apply belt-specific transforms (shear + scale + z-shift) to the slicing trafo. + // Pre-slice remap is handled separately (standalone feature). + // has_remap: whether pre-slice remap was already applied (affects z-shift detection). void apply_to_trafo(Transform3d &trafo, const ModelVolumePtrs &model_volumes, + bool has_remap, double *out_belt_min_z) const; private: explicit BeltSliceStrategy(const PrintConfig &config); - bool m_has_remap = false; bool m_has_shear = false; bool m_has_scale = false; - Transform3d m_pre_remap = Transform3d::Identity(); Matrix3d m_shear = Matrix3d::Identity(); Matrix3d m_scale = Matrix3d::Identity(); }; diff --git a/src/libslic3r/BeltTransform.cpp b/src/libslic3r/BeltTransform.cpp index db7eea7d80..9bb66fcfb6 100644 --- a/src/libslic3r/BeltTransform.cpp +++ b/src/libslic3r/BeltTransform.cpp @@ -13,9 +13,9 @@ Transform3d BeltTransformPipeline::build_preslice_remap(const PrintConfig &confi if (!has_preslice_remap(config)) return pre_remap; - int pre_rx = int(config.belt_preslice_remap_x.value); - int pre_ry = int(config.belt_preslice_remap_y.value); - int pre_rz = int(config.belt_preslice_remap_z.value); + int pre_rx = int(config.preslice_remap_x.value); + int pre_ry = int(config.preslice_remap_y.value); + int pre_rz = int(config.preslice_remap_z.value); // Each remap value selects a source axis and sign. auto remap_column = [](int r) -> Vec3d { @@ -114,13 +114,13 @@ Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &co BoundingBoxf3 BeltTransformPipeline::remap_bbox(const BoundingBoxf3 &bb, const PrintConfig &config) { - int pre_rx = int(config.belt_preslice_remap_x.value); - int pre_ry = int(config.belt_preslice_remap_y.value); - int pre_rz = int(config.belt_preslice_remap_z.value); + int pre_rx = int(config.preslice_remap_x.value); + int pre_ry = int(config.preslice_remap_y.value); + int pre_rz = int(config.preslice_remap_z.value); - if (pre_rx == int(BeltRemapAxis::PosX) && - pre_ry == int(BeltRemapAxis::PosY) && - pre_rz == int(BeltRemapAxis::PosZ)) + if (pre_rx == int(RemapAxis::PosX) && + pre_ry == int(RemapAxis::PosY) && + pre_rz == int(RemapAxis::PosZ)) return bb; // Identity remap. auto remap_coord = [](int r, const Vec3d &v) -> double { diff --git a/src/libslic3r/BeltTransform.hpp b/src/libslic3r/BeltTransform.hpp index b9a60473e5..3c0665c252 100644 --- a/src/libslic3r/BeltTransform.hpp +++ b/src/libslic3r/BeltTransform.hpp @@ -58,21 +58,21 @@ public: static bool has_preslice_remap(const PrintConfig &config) { - return int(config.belt_preslice_remap_x.value) != int(BeltRemapAxis::PosX) || - int(config.belt_preslice_remap_y.value) != int(BeltRemapAxis::PosY) || - int(config.belt_preslice_remap_z.value) != int(BeltRemapAxis::PosZ); + return int(config.preslice_remap_x.value) != int(RemapAxis::PosX) || + int(config.preslice_remap_y.value) != int(RemapAxis::PosY) || + int(config.preslice_remap_z.value) != int(RemapAxis::PosZ); } // Overload accepting DynamicPrintConfig (used in static slicing_parameters). static bool has_preslice_remap(const DynamicPrintConfig &config) { auto get_int = [&](const char *key) -> int { - auto *opt = config.option>(key); + auto *opt = config.option>(key); return opt ? int(opt->value) : 0; }; - return get_int("belt_preslice_remap_x") != int(BeltRemapAxis::PosX) || - get_int("belt_preslice_remap_y") != int(BeltRemapAxis::PosY) || - get_int("belt_preslice_remap_z") != int(BeltRemapAxis::PosZ); + return get_int("preslice_remap_x") != int(RemapAxis::PosX) || + get_int("preslice_remap_y") != int(RemapAxis::PosY) || + get_int("preslice_remap_z") != int(RemapAxis::PosZ); } static bool has_shear(const PrintConfig &config) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a330fad689..93b87cbba8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2425,6 +2425,19 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Belt printer: initialize belt-specific writer via virtual hook. this->init_belt_writer(print, is_bbl_printers); + // Standalone axis remap (works with or without belt mode). + { + 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)); + } + } + // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. m_layer_count = 0; diff --git a/src/libslic3r/GCode/BeltBackTransform.cpp b/src/libslic3r/GCode/BeltBackTransform.cpp index 2e2cd26f75..b9cdac779f 100644 --- a/src/libslic3r/GCode/BeltBackTransform.cpp +++ b/src/libslic3r/GCode/BeltBackTransform.cpp @@ -8,7 +8,7 @@ bool BeltBackTransform::init_from_config(const PrintConfig &config) m_active = false; m_inverse = Transform3d::Identity(); - if (!config.belt_printer.value || !config.belt_gcode_back_transform.value) + if (!config.belt_printer.value || !config.gcode_back_transform.value) return false; // Require at least one active transform to proceed. diff --git a/src/libslic3r/GCode/BeltBackTransform.hpp b/src/libslic3r/GCode/BeltBackTransform.hpp index 5a75d94dc8..b98770c688 100644 --- a/src/libslic3r/GCode/BeltBackTransform.hpp +++ b/src/libslic3r/GCode/BeltBackTransform.hpp @@ -15,7 +15,7 @@ namespace Slic3r { // Initialized once from PrintConfig, then applied per-point in // GCodeWriter::to_machine_coords() before axis remapping. // -// Active when belt_gcode_back_transform is true AND at least one of: +// Active when gcode_back_transform is true AND at least one of: // - a shear axis has global mode enabled, or // - a pre-slice axis remap is non-identity. class BeltBackTransform diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 2288c9e670..e6b6403be5 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3129,26 +3129,26 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers try { m_result.belt_scale_z_angle = std::stof(std::string(comment.substr(22))); } catch (...) {} return; } // Pre-slice axis remap - auto parse_remap_axis = [](const std::string &s) -> BeltRemapAxis { - if (s == "pos_x") return BeltRemapAxis::PosX; - if (s == "pos_y") return BeltRemapAxis::PosY; - if (s == "pos_z") return BeltRemapAxis::PosZ; - if (s == "neg_x") return BeltRemapAxis::NegX; - if (s == "neg_y") return BeltRemapAxis::NegY; - if (s == "neg_z") return BeltRemapAxis::NegZ; - if (s == "rev_x") return BeltRemapAxis::RevX; - if (s == "rev_y") return BeltRemapAxis::RevY; - if (s == "rev_z") return BeltRemapAxis::RevZ; - return BeltRemapAxis::PosX; + auto parse_remap_axis = [](const std::string &s) -> RemapAxis { + if (s == "pos_x") return RemapAxis::PosX; + if (s == "pos_y") return RemapAxis::PosY; + if (s == "pos_z") return RemapAxis::PosZ; + if (s == "neg_x") return RemapAxis::NegX; + if (s == "neg_y") return RemapAxis::NegY; + if (s == "neg_z") return RemapAxis::NegZ; + if (s == "rev_x") return RemapAxis::RevX; + if (s == "rev_y") return RemapAxis::RevY; + if (s == "rev_z") return RemapAxis::RevZ; + return RemapAxis::PosX; }; - if (boost::starts_with(comment, " belt_preslice_remap_x = ")) { - m_result.belt_preslice_remap_x = parse_remap_axis(trim(std::string(comment.substr(25)))); return; + if (boost::starts_with(comment, " preslice_remap_x = ")) { + m_result.preslice_remap_x = parse_remap_axis(trim(std::string(comment.substr(25)))); return; } - if (boost::starts_with(comment, " belt_preslice_remap_y = ")) { - m_result.belt_preslice_remap_y = parse_remap_axis(trim(std::string(comment.substr(25)))); return; + if (boost::starts_with(comment, " preslice_remap_y = ")) { + m_result.preslice_remap_y = parse_remap_axis(trim(std::string(comment.substr(25)))); return; } - if (boost::starts_with(comment, " belt_preslice_remap_z = ")) { - m_result.belt_preslice_remap_z = parse_remap_axis(trim(std::string(comment.substr(25)))); return; + if (boost::starts_with(comment, " preslice_remap_z = ")) { + m_result.preslice_remap_z = parse_remap_axis(trim(std::string(comment.substr(25)))); return; } } // wipe start tag diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 7d08a9cdfd..4c6e4f6618 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -246,9 +246,9 @@ class Print; float belt_scale_y_angle{ 45.f }; BeltScaleMode belt_scale_z{ BeltScaleMode::None }; float belt_scale_z_angle{ 45.f }; - BeltRemapAxis belt_preslice_remap_x{ BeltRemapAxis::PosX }; - BeltRemapAxis belt_preslice_remap_y{ BeltRemapAxis::PosY }; - BeltRemapAxis belt_preslice_remap_z{ BeltRemapAxis::PosZ }; + RemapAxis preslice_remap_x{ RemapAxis::PosX }; + RemapAxis preslice_remap_y{ RemapAxis::PosY }; + RemapAxis preslice_remap_z{ RemapAxis::PosZ }; SettingsIds settings_ids; size_t filaments_count; bool backtrace_enabled; @@ -324,9 +324,9 @@ class Print; belt_scale_y_angle = other.belt_scale_y_angle; belt_scale_z = other.belt_scale_z; belt_scale_z_angle = other.belt_scale_z_angle; - belt_preslice_remap_x = other.belt_preslice_remap_x; - belt_preslice_remap_y = other.belt_preslice_remap_y; - belt_preslice_remap_z = other.belt_preslice_remap_z; + preslice_remap_x = other.preslice_remap_x; + preslice_remap_y = other.preslice_remap_y; + preslice_remap_z = other.preslice_remap_z; #if ENABLE_GCODE_VIEWER_STATISTICS time = other.time; #endif diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 51f82ea009..424d0b722d 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -20,6 +20,36 @@ namespace Slic3r { bool GCodeWriter::full_gcode_comment = true; +void GCodeWriter::set_axis_remap(int rx, int ry, int rz) +{ + m_remap_x = rx; + m_remap_y = ry; + m_remap_z = rz; +} + +void GCodeWriter::set_build_volume_max(const Vec3d &max) +{ + m_build_vol_max = max; +} + +bool GCodeWriter::has_axis_remap() const +{ + return m_remap_x != 0 || m_remap_y != 1 || m_remap_z != 2; +} + +Vec3d GCodeWriter::apply_axis_remap(const Vec3d &pos) const +{ + if (!has_axis_remap()) + return pos; + auto remap = [this, &pos](int r) -> double { + int axis = r % 3; + if (r < 3) return pos[axis]; + if (r < 6) return -pos[axis]; + return m_build_vol_max[axis] - pos[axis]; + }; + return { remap(m_remap_x), remap(m_remap_y), remap(m_remap_z) }; +} + bool GCodeWriter::supports_separate_travel_acceleration(GCodeFlavor flavor) { return (flavor == gcfRepetier || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware); @@ -562,7 +592,13 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset }; GCodeG1Formatter w; - w.emit_xy(point_on_plate); + if (has_axis_remap()) { + // Axis remap may couple XY with Z; emit full XYZ in machine coordinates. + Vec3d machine = apply_axis_remap(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z())); + w.emit_xyz(machine); + } else { + w.emit_xy(point_on_plate); + } auto speed = m_is_first_layer ? this->config.get_abs_value("initial_layer_travel_speed") : this->config.travel_speed.value; w.emit_f(speed * 60.0); @@ -794,7 +830,13 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) } GCodeG1Formatter w; - w.emit_z(z); + if (has_axis_remap()) { + // Remap may couple Z with other axes; emit full XYZ. + Vec3d machine = apply_axis_remap(Vec3d(m_pos.x() - m_x_offset, m_pos.y() - m_y_offset, z)); + w.emit_xyz(machine); + } else { + w.emit_z(z); + } w.emit_f(speed * 60.0); //BBS w.emit_comment(GCodeWriter::full_gcode_comment, comment); @@ -899,7 +941,12 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset }; GCodeG1Formatter w; - w.emit_xy(point_on_plate); + if (has_axis_remap()) { + Vec3d machine = apply_axis_remap(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z())); + w.emit_xyz(machine); + } else { + w.emit_xy(point_on_plate); + } if (!force_no_extrusion) w.emit_e(filament()->E()); //BBS @@ -939,6 +986,9 @@ 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; w.emit_xyz(point_on_plate); if (!force_no_extrusion) diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index c96c3ca2c1..6aa2ee8ffd 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -125,6 +125,12 @@ public: void set_is_first_layer(bool bval) { m_is_first_layer = bval; } GCodeFlavor get_gcode_flavor() const { return config.gcode_flavor; } + // Axis remap: permute/negate/reverse axes in G-code output. + // Works standalone (without belt mode) for printers with non-standard axis conventions. + void set_axis_remap(int rx, int ry, int rz); + void set_build_volume_max(const Vec3d &max); + bool has_axis_remap() const; + // Returns whether this flavor supports separate print and travel acceleration. static bool supports_separate_travel_acceleration(GCodeFlavor flavor); protected: @@ -141,6 +147,15 @@ protected: virtual std::string _travel_to_z(double z, const std::string &comment); + // Axis remap state — accessible to subclasses. + int m_remap_x = 0; // RemapAxis: 0=+X, 1=+Y, 2=+Z, 3=-X, etc. + int m_remap_y = 1; + int m_remap_z = 2; + Vec3d m_build_vol_max = Vec3d::Zero(); + + // Apply axis remap to a point. Returns pos unchanged if remap is identity. + Vec3d apply_axis_remap(const Vec3d &pos) const; + private: // Extruders are sorted by their ID, so that binary search is possible. std::vector m_filament_extruders; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 737ee2d663..399480075e 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1014,8 +1014,8 @@ static std::vector s_Preset_printer_options { "belt_shear_y", "belt_shear_y_angle", "belt_shear_y_from", "belt_shear_y_global", "belt_shear_z", "belt_shear_z_angle", "belt_shear_z_from", "belt_shear_z_global", "belt_scale_x", "belt_scale_x_angle", "belt_scale_y", "belt_scale_y_angle", "belt_scale_z", "belt_scale_z_angle", - "belt_preslice_remap_x", "belt_preslice_remap_y", "belt_preslice_remap_z", - "belt_gcode_remap_x", "belt_gcode_remap_y", "belt_gcode_remap_z", "belt_gcode_back_transform", + "preslice_remap_x", "preslice_remap_y", "preslice_remap_z", + "gcode_remap_x", "gcode_remap_y", "gcode_remap_z", "gcode_back_transform", "belt_origin_snap_x", "belt_origin_offset_x", "belt_origin_snap_y", "belt_origin_offset_y", "belt_origin_snap_z", "belt_origin_offset_z", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1f787a70cf..aca40e4952 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -101,9 +101,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n // or they are only notes not influencing the generated G-code. static std::unordered_set steps_gcode = { // Belt printer G-code axis remap (only affects G-code output, not slicing). - "belt_gcode_remap_x", - "belt_gcode_remap_y", - "belt_gcode_remap_z", + "gcode_remap_x", + "gcode_remap_y", + "gcode_remap_z", "belt_origin_snap_x", "belt_origin_offset_x", "belt_origin_snap_y", "belt_origin_offset_y", "belt_origin_snap_z", "belt_origin_offset_z", @@ -308,9 +308,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "belt_scale_y_angle" || opt_key == "belt_scale_z" || opt_key == "belt_scale_z_angle" - || opt_key == "belt_preslice_remap_x" - || opt_key == "belt_preslice_remap_y" - || opt_key == "belt_preslice_remap_z") { + || opt_key == "preslice_remap_x" + || opt_key == "preslice_remap_y" + || opt_key == "preslice_remap_z") { osteps.emplace_back(posSlice); } else if ( opt_key == "belt_support_floor_offset" diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8c637b7f7c..0cb46c02f6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -313,18 +313,18 @@ static t_config_enum_values s_keys_map_BeltScaleMode { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltScaleMode) -static t_config_enum_values s_keys_map_BeltRemapAxis { - { "pos_x", int(BeltRemapAxis::PosX) }, - { "pos_y", int(BeltRemapAxis::PosY) }, - { "pos_z", int(BeltRemapAxis::PosZ) }, - { "neg_x", int(BeltRemapAxis::NegX) }, - { "neg_y", int(BeltRemapAxis::NegY) }, - { "neg_z", int(BeltRemapAxis::NegZ) }, - { "rev_x", int(BeltRemapAxis::RevX) }, - { "rev_y", int(BeltRemapAxis::RevY) }, - { "rev_z", int(BeltRemapAxis::RevZ) }, +static t_config_enum_values s_keys_map_RemapAxis { + { "pos_x", int(RemapAxis::PosX) }, + { "pos_y", int(RemapAxis::PosY) }, + { "pos_z", int(RemapAxis::PosZ) }, + { "neg_x", int(RemapAxis::NegX) }, + { "neg_y", int(RemapAxis::NegY) }, + { "neg_z", int(RemapAxis::NegZ) }, + { "rev_x", int(RemapAxis::RevX) }, + { "rev_y", int(RemapAxis::RevY) }, + { "rev_z", int(RemapAxis::RevZ) }, }; -CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltRemapAxis) +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(RemapAxis) static t_config_enum_values s_keys_map_BeltSupportFloorMode { { "none", int(BeltSupportFloorMode::None) }, @@ -6124,49 +6124,49 @@ void PrintConfigDef::init_fff_params() add_belt_scale_angle("belt_scale_z_angle", "Angle"); // G-code axis remap with sign - auto add_belt_remap = [this](const char *key, const char *label, const char *tooltip, BeltRemapAxis default_axis) { + auto add_belt_remap = [this](const char *key, const char *label, const char *tooltip, RemapAxis default_axis) { auto def = this->add(key, coEnum); def->label = L(label); def->category = L("Printable space"); def->tooltip = L(tooltip); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values = {"pos_x", "pos_y", "pos_z", "neg_x", "neg_y", "neg_z", "rev_x", "rev_y", "rev_z"}; def->enum_labels = {L("+X"), L("+Y"), L("+Z"), L("-X"), L("-Y"), L("-Z"), L("Rev X"), L("Rev Y"), L("Rev Z")}; - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionEnum(default_axis)); + def->mode = comSimple; // Visibility controlled by toggle_line in Tab.cpp + def->set_default_value(new ConfigOptionEnum(default_axis)); }; - add_belt_remap("belt_preslice_remap_x", "X", + add_belt_remap("preslice_remap_x", "X", "Before slicing, which model-space axis becomes the slicer's X axis. " "Use this to re-orient the coordinate system so the slicer's XY plane matches " "your belt printer's physical bed plane. For a printer whose bed is in the XZ plane, " "set Y to +Z and Z to +Y (or -Y) to swap the vertical and belt-travel axes. " "Default +X: no change.", - BeltRemapAxis::PosX); - add_belt_remap("belt_preslice_remap_y", "Y", + RemapAxis::PosX); + add_belt_remap("preslice_remap_y", "Y", "Before slicing, which model-space axis becomes the slicer's Y axis. " "The slicer treats Y as one of the two horizontal bed axes. If your physical " "belt surface runs along the Z axis, map Y to +Z here so the slicer slices " "along the correct plane. Default +Y: no change.", - BeltRemapAxis::PosY); - add_belt_remap("belt_preslice_remap_z", "Z", + RemapAxis::PosY); + add_belt_remap("preslice_remap_z", "Z", "Before slicing, which model-space axis becomes the slicer's Z axis (layer stacking direction). " "The slicer builds layers upward along this axis. If your printer's layer-stacking " "direction is the physical Y axis, map Z to +Y (or -Y for inverted direction). " "Rev mode mirrors relative to the build volume maximum. Default +Z: no change.", - BeltRemapAxis::PosZ); + RemapAxis::PosZ); - add_belt_remap("belt_gcode_remap_x", "X", "Which slicing axis maps to machine X in G-code output. Applied AFTER slicing, during G-code generation.", BeltRemapAxis::PosX); - add_belt_remap("belt_gcode_remap_y", "Y", "Which slicing axis maps to machine Y in G-code output. Applied AFTER slicing, during G-code generation.", BeltRemapAxis::PosY); - add_belt_remap("belt_gcode_remap_z", "Z", "Which slicing axis maps to machine Z in G-code output. Applied AFTER slicing, during G-code generation.", BeltRemapAxis::PosZ); + add_belt_remap("gcode_remap_x", "X", "Which slicing axis maps to machine X in G-code output. Applied AFTER slicing, during G-code generation.", RemapAxis::PosX); + add_belt_remap("gcode_remap_y", "Y", "Which slicing axis maps to machine Y in G-code output. Applied AFTER slicing, during G-code generation.", RemapAxis::PosY); + add_belt_remap("gcode_remap_z", "Z", "Which slicing axis maps to machine Z in G-code output. Applied AFTER slicing, during G-code generation.", RemapAxis::PosZ); - def = this->add("belt_gcode_back_transform", coBool); + def = this->add("gcode_back_transform", coBool); def->label = L("G-code back-transform"); def->category = L("Printable space"); def->tooltip = L("Reverse the shear/scale transform applied during slicing so G-code " "coordinates are in the machine's physical coordinate space. " "Requires at least one shear axis with global mode enabled."); - def->mode = comAdvanced; + def->mode = comSimple; // Visibility controlled by toggle_line in Tab.cpp def->set_default_value(new ConfigOptionBool(false)); auto add_belt_origin_snap = [this](const char *key_snap, const char *key_offset, diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 13202657ef..5d9f977ed1 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -181,7 +181,7 @@ enum class BeltAxis Z = 2, }; -enum class BeltRemapAxis +enum class RemapAxis { PosX = 0, PosY = 1, PosZ = 2, NegX = 3, NegY = 4, NegZ = 5, @@ -549,7 +549,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SlicingMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltShearMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltScaleMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltAxis) -CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltRemapAxis) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(RemapAxis) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportFloorMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportZOffsetMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportMaterialPattern) @@ -1487,13 +1487,13 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloat, belt_scale_y_angle)) ((ConfigOptionEnum, belt_scale_z)) ((ConfigOptionFloat, belt_scale_z_angle)) - ((ConfigOptionEnum, belt_preslice_remap_x)) - ((ConfigOptionEnum, belt_preslice_remap_y)) - ((ConfigOptionEnum, belt_preslice_remap_z)) - ((ConfigOptionEnum, belt_gcode_remap_x)) - ((ConfigOptionEnum, belt_gcode_remap_y)) - ((ConfigOptionEnum, belt_gcode_remap_z)) - ((ConfigOptionBool, belt_gcode_back_transform)) + ((ConfigOptionEnum, preslice_remap_x)) + ((ConfigOptionEnum, preslice_remap_y)) + ((ConfigOptionEnum, preslice_remap_z)) + ((ConfigOptionEnum, gcode_remap_x)) + ((ConfigOptionEnum, gcode_remap_y)) + ((ConfigOptionEnum, gcode_remap_z)) + ((ConfigOptionBool, gcode_back_transform)) ((ConfigOptionBool, belt_origin_snap_x)) ((ConfigOptionFloat, belt_origin_offset_x)) ((ConfigOptionBool, belt_origin_snap_y)) diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 3067772f09..7279b83d14 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -144,11 +144,31 @@ static std::vector slice_volumes_inner( params_base.closing_radius = print_object_config.slice_closing_radius.value; params_base.extra_offset = 0; params_base.trafo = object_trafo; + // Standalone pre-slice axis remap (works without belt mode). + bool has_remap = BeltTransformPipeline::has_preslice_remap(print_config); + if (has_remap) + params_base.trafo = BeltTransformPipeline::build_preslice_remap(print_config) * params_base.trafo; { - // Belt printer: apply pre-slice transforms (remap, shear, scale, z-shift) via strategy. + // Belt-specific transforms (shear, scale, z-shift) via strategy. auto belt_strategy = BeltSliceStrategy::create(print_config); if (belt_strategy) - belt_strategy->apply_to_trafo(params_base.trafo, model_volumes, out_belt_min_z); + belt_strategy->apply_to_trafo(params_base.trafo, model_volumes, has_remap, out_belt_min_z); + } + // If only remap (no belt), still need z-shift detection. + if (has_remap && !print_config.belt_printer.value) { + double min_z = std::numeric_limits::max(); + for (const ModelVolume *mv : model_volumes) { + if (!mv->is_model_part()) continue; + for (const stl_vertex &v : mv->mesh().its.vertices) { + Vec3d pt = params_base.trafo * v.cast(); + min_z = std::min(min_z, pt.z()); + } + } + if (min_z < 0. && min_z != std::numeric_limits::max()) { + Transform3d z_shift = Transform3d::Identity(); + z_shift.matrix()(2, 3) = -min_z; + params_base.trafo = z_shift * params_base.trafo; + } } //BBS: 0.0025mm is safe enough to simplify the data to speed slicing up for high-resolution model. //Also has on influence on arc fitting which has default resolution 0.0125mm. diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 202c28c121..e43e979955 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4436,19 +4436,19 @@ void TabPrinter::build_fff() L("Remap model axes before slicing so the slicer's coordinate system matches " "the physical bed orientation. For belt printers whose bed is NOT in the XY plane, " "use this to swap axes so layers are stacked in the correct physical direction.") }; - line.append_option(optgroup->get_option("belt_preslice_remap_x")); - line.append_option(optgroup->get_option("belt_preslice_remap_y")); - line.append_option(optgroup->get_option("belt_preslice_remap_z")); + line.append_option(optgroup->get_option("preslice_remap_x")); + line.append_option(optgroup->get_option("preslice_remap_y")); + line.append_option(optgroup->get_option("preslice_remap_z")); optgroup->append_line(line); } { Line line = { L("G-code axis remap (post-slice)"), L("Remap slicing-frame axes to machine axes in G-code output. Applied AFTER slicing, during G-code generation.") }; - line.append_option(optgroup->get_option("belt_gcode_remap_x")); - line.append_option(optgroup->get_option("belt_gcode_remap_y")); - line.append_option(optgroup->get_option("belt_gcode_remap_z")); + line.append_option(optgroup->get_option("gcode_remap_x")); + line.append_option(optgroup->get_option("gcode_remap_y")); + line.append_option(optgroup->get_option("gcode_remap_z")); optgroup->append_line(line); } - optgroup->append_single_option_line("belt_gcode_back_transform"); + optgroup->append_single_option_line("gcode_back_transform"); { Line line = { L("Origin snap X"), L("Snap object bbox min X to offset in G-code output") }; line.append_option(optgroup->get_option("belt_origin_snap_x")); @@ -5321,11 +5321,14 @@ void TabPrinter::toggle_options() toggle_line("belt_printer_infinite_y", is_belt); for (auto el : {"belt_shear_x", "belt_shear_y", "belt_shear_z", "belt_scale_x", "belt_scale_y", "belt_scale_z", - "belt_preslice_remap_x", - "belt_gcode_remap_x", "belt_gcode_back_transform", "belt_origin_snap_x", "belt_origin_snap_y", "belt_origin_snap_z"}) toggle_line(el, is_belt); + // Remap options: visible when belt mode is on OR when UI is in developer mode. + bool show_remap = is_belt || (m_mode >= comDevelop); + for (auto el : {"preslice_remap_x", "gcode_remap_x", "gcode_back_transform"}) + toggle_line(el, show_remap); + // Gray out angle/from sub-options when their parent shear/scale mode is None. auto sx = m_config->option>("belt_shear_x")->value; toggle_option("belt_shear_x_angle", is_belt && sx != BeltShearMode::None);