mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 13:32:44 +00:00
Part 3.2: decouple axis remapping, enable viewing settings in Developer mode or when Belt mode is active
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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.};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user