Part 3.2: decouple axis remapping, enable viewing settings in Developer mode or when Belt mode is active

This commit is contained in:
harrierpigeon
2026-04-09 23:07:07 -05:00
parent 2facaac9e8
commit c8a1bf3a99
20 changed files with 215 additions and 154 deletions
+4 -10
View File
@@ -15,13 +15,7 @@ void BeltGCode::init_belt_writer(Print &print, bool is_bbl_printers)
auto belt_writer = std::make_unique<BeltGCodeWriter>();
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)
+3 -22
View File
@@ -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]);
+1 -7
View File
@@ -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.};
+4 -11
View File
@@ -13,31 +13,24 @@ std::unique_ptr<BeltSliceStrategy> 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<double>::max();
for (const ModelVolume *mv : model_volumes) {
if (!mv->is_model_part()) continue;
+4 -6
View File
@@ -23,21 +23,19 @@ public:
// Create a strategy if belt_printer is enabled; returns nullptr otherwise.
static std::unique_ptr<BeltSliceStrategy> 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();
};
+9 -9
View File
@@ -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 {
+7 -7
View File
@@ -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<ConfigOptionEnum<BeltRemapAxis>>(key);
auto *opt = config.option<ConfigOptionEnum<RemapAxis>>(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)
+13
View File
@@ -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;
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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
+17 -17
View File
@@ -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
+6 -6
View File
@@ -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
+53 -3
View File
@@ -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)
+15
View File
@@ -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<Extruder> m_filament_extruders;
+2 -2
View File
@@ -1014,8 +1014,8 @@ static std::vector<std::string> 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",
+6 -6
View File
@@ -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<std::string> 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"
+26 -26
View File
@@ -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<BeltRemapAxis>::get_enum_values();
def->enum_keys_map = &ConfigOptionEnum<RemapAxis>::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<BeltRemapAxis>(default_axis));
def->mode = comSimple; // Visibility controlled by toggle_line in Tab.cpp
def->set_default_value(new ConfigOptionEnum<RemapAxis>(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,
+9 -9
View File
@@ -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<BeltScaleMode>, belt_scale_z))
((ConfigOptionFloat, belt_scale_z_angle))
((ConfigOptionEnum<BeltRemapAxis>, belt_preslice_remap_x))
((ConfigOptionEnum<BeltRemapAxis>, belt_preslice_remap_y))
((ConfigOptionEnum<BeltRemapAxis>, belt_preslice_remap_z))
((ConfigOptionEnum<BeltRemapAxis>, belt_gcode_remap_x))
((ConfigOptionEnum<BeltRemapAxis>, belt_gcode_remap_y))
((ConfigOptionEnum<BeltRemapAxis>, belt_gcode_remap_z))
((ConfigOptionBool, belt_gcode_back_transform))
((ConfigOptionEnum<RemapAxis>, preslice_remap_x))
((ConfigOptionEnum<RemapAxis>, preslice_remap_y))
((ConfigOptionEnum<RemapAxis>, preslice_remap_z))
((ConfigOptionEnum<RemapAxis>, gcode_remap_x))
((ConfigOptionEnum<RemapAxis>, gcode_remap_y))
((ConfigOptionEnum<RemapAxis>, gcode_remap_z))
((ConfigOptionBool, gcode_back_transform))
((ConfigOptionBool, belt_origin_snap_x))
((ConfigOptionFloat, belt_origin_offset_x))
((ConfigOptionBool, belt_origin_snap_y))
+22 -2
View File
@@ -144,11 +144,31 @@ static std::vector<VolumeSlices> 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<double>::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<double>();
min_z = std::min(min_z, pt.z());
}
}
if (min_z < 0. && min_z != std::numeric_limits<double>::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.
+12 -9
View File
@@ -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<ConfigOptionEnum<BeltShearMode>>("belt_shear_x")->value;
toggle_option("belt_shear_x_angle", is_belt && sx != BeltShearMode::None);