diff --git a/resources/profiles/Custom/machine/fdm_belt_common.json b/resources/profiles/Custom/machine/fdm_belt_common.json index 0530999f15..b010742d78 100644 --- a/resources/profiles/Custom/machine/fdm_belt_common.json +++ b/resources/profiles/Custom/machine/fdm_belt_common.json @@ -93,8 +93,6 @@ "belt_slice_rotation_angle": "45", "belt_slice_rotation_global": "1", "build_plate_tilt_x": "45", - "gcode_shear_z": "pos_tan", - "gcode_scale_y": "inv_cos", "purge_in_prime_tower": "0", "scan_first_layer": "0", "auxiliary_fan": "0" diff --git a/src/libslic3r/BeltGCode.cpp b/src/libslic3r/BeltGCode.cpp index 8184692862..3ba2f79e55 100644 --- a/src/libslic3r/BeltGCode.cpp +++ b/src/libslic3r/BeltGCode.cpp @@ -35,23 +35,10 @@ void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print) file.write_format("; preslice_remap_z = %s\n", full_cfg.opt_serialize("preslice_remap_z").c_str()); file.write_format("; preslice_remap_global = %d\n", print.config().preslice_remap_global.value ? 1 : 0); file.write_format("; belt_preslice_global = %d\n", print.config().belt_preslice_global.value ? 1 : 0); - // Machine-frame transform configs - file.write_format("; gcode_shear_x = %s\n", full_cfg.opt_serialize("gcode_shear_x").c_str()); - file.write_format("; gcode_shear_x_angle = %.1f\n", print.config().gcode_shear_x_angle.value); - file.write_format("; gcode_shear_x_from = %s\n", full_cfg.opt_serialize("gcode_shear_x_from").c_str()); - file.write_format("; gcode_shear_y = %s\n", full_cfg.opt_serialize("gcode_shear_y").c_str()); - file.write_format("; gcode_shear_y_angle = %.1f\n", print.config().gcode_shear_y_angle.value); - file.write_format("; gcode_shear_y_from = %s\n", full_cfg.opt_serialize("gcode_shear_y_from").c_str()); - file.write_format("; gcode_shear_z = %s\n", full_cfg.opt_serialize("gcode_shear_z").c_str()); - file.write_format("; gcode_shear_z_angle = %.1f\n", print.config().gcode_shear_z_angle.value); - file.write_format("; gcode_shear_z_from = %s\n", full_cfg.opt_serialize("gcode_shear_z_from").c_str()); - file.write_format("; gcode_scale_x = %s\n", full_cfg.opt_serialize("gcode_scale_x").c_str()); - file.write_format("; gcode_scale_x_angle = %.1f\n", print.config().gcode_scale_x_angle.value); - file.write_format("; gcode_scale_y = %s\n", full_cfg.opt_serialize("gcode_scale_y").c_str()); - file.write_format("; gcode_scale_y_angle = %.1f\n", print.config().gcode_scale_y_angle.value); - file.write_format("; gcode_scale_z = %s\n", full_cfg.opt_serialize("gcode_scale_z").c_str()); - file.write_format("; gcode_scale_z_angle = %.1f\n", print.config().gcode_scale_z_angle.value); - file.write_format("; belt_gcode_transform_order = %s\n", full_cfg.opt_serialize("belt_gcode_transform_order").c_str()); + // Machine-frame transform: shear (tan) + scale (1/cos) derived from the belt + // tilt angle (or belt_frame_tilt_angle when decoupled). + file.write_format("; belt_frame_tilt_decouple = %d\n", print.config().belt_frame_tilt_decouple.value ? 1 : 0); + file.write_format("; belt_frame_tilt_angle = %.1f\n", print.config().belt_frame_tilt_angle.value); } void BeltGCode::on_set_origin(const PrintObject * /*obj*/, const Point & /*inst_shift*/) diff --git a/src/libslic3r/BeltTransform.hpp b/src/libslic3r/BeltTransform.hpp index 50f56bd75f..b67ac2007d 100644 --- a/src/libslic3r/BeltTransform.hpp +++ b/src/libslic3r/BeltTransform.hpp @@ -21,44 +21,11 @@ class ModelObject; // to the g-code instead (see MachineFrameTransform). This class provides the // building blocks so every call site uses the same implementation. z_shift is // object-dependent (computed from mesh vertex bounds) and is NOT included in -// build_forward_transform(). -// -// The compute_shear_factor / compute_scale_factor math helpers below are still -// used by the g-code-side machine-frame shear/scale. +// build_forward_transform(). The machine-frame shear/scale is derived directly +// from the tilt angle in MachineFrameTransform and no longer lives here. class BeltTransformPipeline { public: - // ---- Pure math helpers ------------------------------------------------ - - static double compute_shear_factor(BeltShearMode mode, double angle_deg) - { - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad); - double cos_a = std::cos(angle_rad); - switch (mode) { - case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.; - case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.; - case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.; - case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.; - default: return 0.; - } - } - - static double compute_scale_factor(BeltScaleMode mode, double angle_deg) - { - if (mode == BeltScaleMode::None) return 1.; - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad); - double cos_a = std::cos(angle_rad); - switch (mode) { - case BeltScaleMode::InvSin: return (sin_a > EPSILON) ? 1. / sin_a : 1.; - case BeltScaleMode::InvCos: return (cos_a > EPSILON) ? 1. / cos_a : 1.; - case BeltScaleMode::Sin: return sin_a; - case BeltScaleMode::Cos: return cos_a; - default: return 1.; - } - } - // ---- Identity checks -------------------------------------------------- static bool has_preslice_remap(const PrintConfig &config) diff --git a/src/libslic3r/GCode/MachineFrameTransform.cpp b/src/libslic3r/GCode/MachineFrameTransform.cpp index 03f74605fe..bdc172d7a4 100644 --- a/src/libslic3r/GCode/MachineFrameTransform.cpp +++ b/src/libslic3r/GCode/MachineFrameTransform.cpp @@ -1,57 +1,10 @@ #include "MachineFrameTransform.hpp" -#include "../BeltTransform.hpp" -#include "../BoundingBox.hpp" +#include "../Geometry.hpp" + +#include namespace Slic3r { -namespace { - -// Build the 3x3 shear matrix from gcode_shear_* keys. -Matrix3d build_gcode_shear_matrix(const PrintConfig &config, bool &active) -{ - struct AxisShear { BeltShearMode mode; double angle; int from; }; - AxisShear axes[3] = { - { config.gcode_shear_x.value, config.gcode_shear_x_angle.value, int(config.gcode_shear_x_from.value) }, - { config.gcode_shear_y.value, config.gcode_shear_y_angle.value, int(config.gcode_shear_y_from.value) }, - { config.gcode_shear_z.value, config.gcode_shear_z_angle.value, int(config.gcode_shear_z_from.value) }, - }; - - Matrix3d shear = Matrix3d::Identity(); - active = false; - for (int row = 0; row < 3; ++row) { - if (axes[row].mode != BeltShearMode::None) { - double factor = BeltTransformPipeline::compute_shear_factor(axes[row].mode, axes[row].angle); - if (std::abs(factor) > EPSILON) { - shear(row, axes[row].from) += factor; - active = true; - } - } - } - return shear; -} - -// Build the 3x3 diagonal scale matrix from gcode_scale_* keys. -Matrix3d build_gcode_scale_matrix(const PrintConfig &config, bool &active) -{ - double sx = BeltTransformPipeline::compute_scale_factor(config.gcode_scale_x.value, config.gcode_scale_x_angle.value); - double sy = BeltTransformPipeline::compute_scale_factor(config.gcode_scale_y.value, config.gcode_scale_y_angle.value); - double sz = BeltTransformPipeline::compute_scale_factor(config.gcode_scale_z.value, config.gcode_scale_z_angle.value); - - active = (std::abs(sx - 1.) > EPSILON || - std::abs(sy - 1.) > EPSILON || - std::abs(sz - 1.) > EPSILON); - - Matrix3d scale = Matrix3d::Identity(); - if (active) { - scale(0, 0) = sx; - scale(1, 1) = sy; - scale(2, 2) = sz; - } - return scale; -} - -} // namespace - bool MachineFrameTransform::init_from_config(const PrintConfig &config) { m_active = false; @@ -60,21 +13,48 @@ bool MachineFrameTransform::init_from_config(const PrintConfig &config) if (!config.belt_printer.value) return false; - bool shear_active = false; - Matrix3d shear = build_gcode_shear_matrix(config, shear_active); - bool scale_active = false; - Matrix3d scale = build_gcode_scale_matrix(config, scale_active); + // The machine-frame transform is derived from the single belt tilt (axis + + // angle) that also drives the pre-slice mesh rotation. Expert decouple lets + // the machine-frame angle differ from the slicing rotation; otherwise both + // use belt_slice_rotation_angle. + const BeltRotationAxis axis = config.belt_slice_rotation.value; + if (axis == BeltRotationAxis::None || axis == BeltRotationAxis::Z) + return false; // Z is an in-plane spin: no machine-frame tilt. - if (!shear_active && !scale_active) + const double angle_deg = config.belt_frame_tilt_decouple.value + ? config.belt_frame_tilt_angle.value + : config.belt_slice_rotation_angle.value; + if (std::abs(angle_deg) <= EPSILON) return false; - // Compose per belt_gcode_transform_order: - // ScaleThenShear: applied to p, scale runs first then shear (shear * scale). - // ShearThenScale: applied to p, shear runs first then scale (scale * shear). + const double angle_rad = Geometry::deg2rad(angle_deg); + const double cos_a = std::cos(angle_rad); + if (std::abs(cos_a) <= EPSILON) + return false; + const double tan_a = std::sin(angle_rad) / cos_a; + const double inv_cos = 1.0 / cos_a; + + // Couple the height axis (Z) to the belt-feed axis and stretch the belt-feed + // axis by 1/cos so a unit slicing move maps to the correct belt travel. The + // shear sign matches the belt-floor slope derived from the same rotation in + // BeltTransformPipeline::compute_belt_height_and_floor: + // tilt about X: feed axis Y, Z += +tan·Y, scale Y *= 1/cos + // tilt about Y: feed axis X, Z += -tan·X, scale X *= 1/cos + Matrix3d shear = Matrix3d::Identity(); + Matrix3d scale = Matrix3d::Identity(); + if (axis == BeltRotationAxis::X) { + shear(2, 1) = tan_a; // Z from Y + scale(1, 1) = inv_cos; // Y + } else { // BeltRotationAxis::Y + shear(2, 0) = -tan_a; // Z from X + scale(0, 0) = inv_cos; // X + } + + // Apply shear first, then scale (the historical default ShearThenScale order: + // result = scale * shear * p). For the canonical 45°/X belt this maps + // (x,y,z) -> (x, y/cos, y + z), matching the previous per-axis config. Transform3d combined = Transform3d::Identity(); - combined.linear() = (config.belt_gcode_transform_order.value == BeltTransformOrder::ScaleThenShear) - ? Matrix3d(shear * scale) - : Matrix3d(scale * shear); + combined.linear() = scale * shear; if (combined.isApprox(Transform3d::Identity())) return false; diff --git a/src/libslic3r/GCode/MachineFrameTransform.hpp b/src/libslic3r/GCode/MachineFrameTransform.hpp index 9fa25b2323..425fe3004b 100644 --- a/src/libslic3r/GCode/MachineFrameTransform.hpp +++ b/src/libslic3r/GCode/MachineFrameTransform.hpp @@ -9,14 +9,15 @@ namespace Slic3r { // Post-stage machine-frame transform for belt printers. // -// Applied in BeltGCodeWriter::to_machine_coords AFTER the back-transform, -// the gcode_remap_* axis remap and per-axis origin snap. Maps Cartesian -// (axis-permuted) G-code coordinates into the printer's physical machine -// frame using: -// gcode_shear_x/y/z + _angle + _from -// gcode_scale_x/y/z + _angle +// Applied in BeltGCodeWriter::to_machine_coords AFTER the back-transform and +// the gcode_remap_* axis remap. Maps Cartesian (axis-permuted) G-code +// coordinates into the printer's physical machine frame. // -// Composition follows belt_gcode_transform_order (shear * scale or scale * shear). +// Derived entirely from the single belt tilt (belt_slice_rotation axis + +// belt_slice_rotation_angle): a shear coupling the height axis to the belt-feed +// axis (factor tan a) plus a 1/cos a scale on the belt-feed axis. The expert +// belt_frame_tilt_decouple flag lets the machine-frame angle differ from the +// pre-slice rotation angle via belt_frame_tilt_angle. class MachineFrameTransform { public: diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 9b514bb291..a9ccfdbd6b 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1330,13 +1330,7 @@ static std::vector s_Preset_printer_options { "belt_slice_rotation", "belt_slice_rotation_angle", "belt_slice_rotation_global", "preslice_remap_x", "preslice_remap_y", "preslice_remap_z", "preslice_remap_global", "gcode_remap_x", "gcode_remap_y", "gcode_remap_z", "gcode_back_transform", - "gcode_shear_x", "gcode_shear_x_angle", "gcode_shear_x_from", - "gcode_shear_y", "gcode_shear_y_angle", "gcode_shear_y_from", - "gcode_shear_z", "gcode_shear_z_angle", "gcode_shear_z_from", - "gcode_scale_x", "gcode_scale_x_angle", - "gcode_scale_y", "gcode_scale_y_angle", - "gcode_scale_z", "gcode_scale_z_angle", - "belt_gcode_transform_order", + "belt_frame_tilt_decouple", "belt_frame_tilt_angle", "belt_preslice_global", "first_layer_plane", "first_layer_plane_offset", "first_layer_plane_thickness", "belt_support_floor_offset", "belt_support_floor_mode", "belt_support_z_offset_mode", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 099a592509..b511898ae8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -104,14 +104,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "gcode_remap_x", "gcode_remap_y", "gcode_remap_z", - // Machine-frame transforms (only affect G-code output, not slicing). - "gcode_shear_x", "gcode_shear_x_angle", "gcode_shear_x_from", - "gcode_shear_y", "gcode_shear_y_angle", "gcode_shear_y_from", - "gcode_shear_z", "gcode_shear_z_angle", "gcode_shear_z_from", - "gcode_scale_x", "gcode_scale_x_angle", - "gcode_scale_y", "gcode_scale_y_angle", - "gcode_scale_z", "gcode_scale_z_angle", - "belt_gcode_transform_order", + // Machine-frame transform (derived from belt tilt; only affects G-code output). + "belt_frame_tilt_decouple", "belt_frame_tilt_angle", //BBS "additional_cooling_fan_speed", "reduce_crossing_wall", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index cdb2ec1d5d..723725a7f2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -312,22 +312,6 @@ static t_config_enum_values s_keys_map_SlicingMode { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SlicingMode) -static t_config_enum_values s_keys_map_BeltShearMode { - { "none", int(BeltShearMode::None) }, - { "pos_cot", int(BeltShearMode::PosCot) }, - { "neg_cot", int(BeltShearMode::NegCot) }, - { "pos_tan", int(BeltShearMode::PosTan) }, - { "neg_tan", int(BeltShearMode::NegTan) }, -}; -CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltShearMode) - -static t_config_enum_values s_keys_map_BeltAxis { - { "x", int(BeltAxis::X) }, - { "y", int(BeltAxis::Y) }, - { "z", int(BeltAxis::Z) }, -}; -CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltAxis) - static t_config_enum_values s_keys_map_BeltRotationAxis { { "none", int(BeltRotationAxis::None) }, { "x", int(BeltRotationAxis::X) }, @@ -336,21 +320,6 @@ static t_config_enum_values s_keys_map_BeltRotationAxis { }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltRotationAxis) -static t_config_enum_values s_keys_map_BeltTransformOrder { - { "scale_then_shear", int(BeltTransformOrder::ScaleThenShear) }, - { "shear_then_scale", int(BeltTransformOrder::ShearThenScale) }, -}; -CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltTransformOrder) - -static t_config_enum_values s_keys_map_BeltScaleMode { - { "none", int(BeltScaleMode::None) }, - { "inv_sin", int(BeltScaleMode::InvSin) }, - { "inv_cos", int(BeltScaleMode::InvCos) }, - { "sin", int(BeltScaleMode::Sin) }, - { "cos", int(BeltScaleMode::Cos) }, -}; -CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltScaleMode) - static t_config_enum_values s_keys_map_RemapAxis { { "pos_x", int(RemapAxis::PosX) }, { "pos_y", int(RemapAxis::PosY) }, @@ -6566,17 +6535,28 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); - auto add_belt_transform_order = [this](const char *key, const char *label, const char *tooltip) { - 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_values = {"scale_then_shear", "shear_then_scale"}; - def->enum_labels = {L("Scale, then shear"), L("Shear, then scale")}; - def->mode = comExpert; - def->set_default_value(new ConfigOptionEnum(BeltTransformOrder::ShearThenScale)); - }; + def = this->add("belt_frame_tilt_decouple", coBool); + def->label = L("Decouple machine-frame tilt"); + def->category = L("Printable space"); + def->tooltip = L("Expert override: set the machine-frame (g-code shear/scale) tilt angle " + "independently of the pre-slice rotation angle. When disabled, the " + "machine-frame transform is derived from the belt tilt angle, so a single " + "angle drives both stages. Enable only to compensate for a machine whose " + "physical gantry tilt differs from the slicing rotation."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("belt_frame_tilt_angle", coFloat); + def->label = L("Machine-frame tilt angle"); + def->category = L("Printable space"); + def->tooltip = L("Tilt angle (degrees) used to derive the machine-frame shear (tan) and " + "scale (1/cos) applied to G-code. Only used when 'Decouple machine-frame " + "tilt' is enabled; otherwise the belt tilt angle is used."); + def->sidetext = L("°"); + def->min = -89.9; + def->max = 89.9; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(45.)); // G-code axis remap with sign auto add_belt_remap = [this](const char *key, const char *label, const char *tooltip, @@ -6626,96 +6606,9 @@ void PrintConfigDef::init_fff_params() 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, comExpert); 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, comExpert); - // Machine-frame G-code transforms: applied AFTER back-transform and gcode_remap, - // before per-axis origin snap. Maps Cartesian G-code to the printer's physical machine frame. - // These shear/scale transforms act on the G-code coordinates (not the mesh) — they are - // the belt printer's only shear/scale stage. - auto add_belt_shear_mode = [this](const char *key, const char *label, BeltShearMode default_mode, - ConfigOptionMode mode = comAdvanced) { - auto def = this->add(key, coEnum); - def->label = L(label); - def->category = L("Printable space"); - def->tooltip = L("Shear function applied to this axis in belt printer mode."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values = {"none", "pos_cot", "neg_cot", "pos_tan", "neg_tan"}; - def->enum_labels = {L("None"), L("+cot(α)"), L("-cot(α)"), L("+tan(α)"), L("-tan(α)")}; - def->mode = mode; - def->set_default_value(new ConfigOptionEnum(default_mode)); - }; - auto add_belt_shear_angle = [this](const char *key, const char *label, - ConfigOptionMode mode = comAdvanced) { - auto def = this->add(key, coFloat); - def->label = L(label); - def->category = L("Printable space"); - def->tooltip = L("Angle (degrees) for the shear function on this axis."); - def->sidetext = L("°"); - def->min = 0.1; - def->max = 89.9; - def->mode = mode; - def->set_default_value(new ConfigOptionFloat(45)); - }; - auto add_belt_axis_enum = [this](const char *key, const char *label, const char *tooltip, - BeltAxis default_axis, ConfigOptionMode mode = comAdvanced) { - 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_values = {"x", "y", "z"}; - def->enum_labels = {L("X"), L("Y"), L("Z")}; - def->mode = mode; - def->set_default_value(new ConfigOptionEnum(default_axis)); - }; - auto add_belt_scale_mode = [this](const char *key, const char *label, BeltScaleMode default_mode, - ConfigOptionMode mode = comAdvanced) { - auto def = this->add(key, coEnum); - def->label = L(label); - def->category = L("Printable space"); - def->tooltip = L("Scale factor applied to this axis in belt printer mode."); - def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values = {"none", "inv_sin", "inv_cos", "sin", "cos"}; - def->enum_labels = {L("None"), L("1/sin(α)"), L("1/cos(α)"), L("sin(α)"), L("cos(α)")}; - def->mode = mode; - def->set_default_value(new ConfigOptionEnum(default_mode)); - }; - auto add_belt_scale_angle = [this](const char *key, const char *label, - ConfigOptionMode mode = comAdvanced) { - auto def = this->add(key, coFloat); - def->label = L(label); - def->category = L("Printable space"); - def->tooltip = L("Angle (degrees) for the scale function on this axis."); - def->sidetext = L("°"); - def->min = 0.1; - def->max = 89.9; - def->mode = mode; - def->set_default_value(new ConfigOptionFloat(45)); - }; - - add_belt_shear_mode ("gcode_shear_x", "Function", BeltShearMode::None, comExpert); - add_belt_shear_angle("gcode_shear_x_angle", "Angle", comExpert); - add_belt_axis_enum ("gcode_shear_x_from", "From", "Source axis for X shear in the machine-frame stage.", BeltAxis::Z, comExpert); - - add_belt_shear_mode ("gcode_shear_y", "Function", BeltShearMode::None, comExpert); - add_belt_shear_angle("gcode_shear_y_angle", "Angle", comExpert); - add_belt_axis_enum ("gcode_shear_y_from", "From", "Source axis for Y shear in the machine-frame stage.", BeltAxis::Z, comExpert); - - add_belt_shear_mode ("gcode_shear_z", "Function", BeltShearMode::None); - add_belt_shear_angle("gcode_shear_z_angle", "Angle"); - add_belt_axis_enum ("gcode_shear_z_from", "From", "Source axis for Z shear in the machine-frame stage.", BeltAxis::Y); - - add_belt_scale_mode ("gcode_scale_x", "Function", BeltScaleMode::None, comExpert); - add_belt_scale_angle("gcode_scale_x_angle", "Angle", comExpert); - - add_belt_scale_mode ("gcode_scale_y", "Function", BeltScaleMode::None); - add_belt_scale_angle("gcode_scale_y_angle", "Angle"); - - add_belt_scale_mode ("gcode_scale_z", "Function", BeltScaleMode::None, comExpert); - add_belt_scale_angle("gcode_scale_z_angle", "Angle", comExpert); - - add_belt_transform_order("belt_gcode_transform_order", "G-code transform order", - "Order in which the machine-frame shear and scale matrices are composed when " - "applied to G-code coordinates. 'Scale, then shear' applies scale first and then " - "shear (current default). 'Shear, then scale' applies shear first and then scale."); + // The machine-frame G-code transform (shear + scale) is no longer configured + // by per-axis keys: it is derived from the belt tilt (belt_slice_rotation axis + // + angle, or belt_frame_tilt_angle when decoupled) in MachineFrameTransform. def = this->add("gcode_back_transform", coBool); def->label = L("G-code back-transform"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index a31179cd9b..47da8f1b55 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -169,34 +169,10 @@ enum class SlicingMode CloseHoles, }; -enum class BeltShearMode -{ - None, // No shear on this axis - PosCot, // += cot(α) - NegCot, // -= cot(α) - PosTan, // += tan(α) - NegTan, // -= tan(α) -}; - -enum class BeltScaleMode -{ - None, // No scaling (factor = 1) - InvSin, // 1/sin(α) - InvCos, // 1/cos(α) - Sin, // sin(α) - Cos, // cos(α) -}; - -enum class BeltAxis -{ - X = 0, - Y = 1, - Z = 2, -}; - // Axis around which the mesh is rotated before slicing, when -// `belt_slice_rotation` is set. None disables the rotation stage. -// Distinct from BeltAxis because BeltAxis carries no "None" semantics. +// `belt_slice_rotation` is set. None disables the rotation stage. This is the +// single "belt tilt" axis: it drives both the pre-slice mesh rotation and the +// post-slice machine-frame transform (shear + scale derived from the tilt angle). enum class BeltRotationAxis { None = 0, @@ -205,15 +181,6 @@ enum class BeltRotationAxis Z = 3, }; -// Order in which the belt shear and scale matrices are composed. -// ScaleThenShear: applied to a point p, the result is shear(scale(p)). -// ShearThenScale: applied to a point p, the result is scale(shear(p)). -enum class BeltTransformOrder -{ - ScaleThenShear = 0, - ShearThenScale = 1, -}; - enum class RemapAxis { PosX = 0, PosY = 1, PosZ = 2, @@ -616,11 +583,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NoiseType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(InfillPattern) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(IroningType) 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(BeltRotationAxis) -CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltTransformOrder) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(RemapAxis) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportFloorMode) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportZOffsetMode) @@ -1582,15 +1545,19 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( // Belt printer settings (printer-level). ((ConfigOptionBool, belt_printer)) ((ConfigOptionBool, belt_printer_infinite_y)) - // Mesh rotation applied before slicing — the sole mesh-side belt transform and - // the single source of truth for the physical belt tilt (its angle + axis drive - // bed rendering, support gravity tilt, and the bed-exclusion projection). - // Isometric (no distortion); the g-code back-transform inverts it before the - // machine-frame remap. (Shear & scale are applied to the g-code, not the - // mesh — see gcode_shear_* / gcode_scale_* below.) + // Mesh rotation applied before slicing — the single source of truth for the + // physical belt tilt. Its angle + axis drive bed rendering, support gravity + // tilt, the bed-exclusion projection, AND the post-slice machine-frame + // transform (shear + scale, derived from the tilt angle; see + // MachineFrameTransform). Isometric (no distortion) on the mesh side; the + // g-code back-transform inverts the rotation before the machine-frame stage. ((ConfigOptionEnum, belt_slice_rotation)) ((ConfigOptionFloat, belt_slice_rotation_angle)) ((ConfigOptionBool, belt_slice_rotation_global)) + // Expert override: decouple the machine-frame tilt angle from the pre-slice + // rotation angle. When disabled, the machine frame uses belt_slice_rotation_angle. + ((ConfigOptionBool, belt_frame_tilt_decouple)) + ((ConfigOptionFloat, belt_frame_tilt_angle)) ((ConfigOptionEnum, preslice_remap_x)) ((ConfigOptionEnum, preslice_remap_y)) ((ConfigOptionEnum, preslice_remap_z)) @@ -1598,22 +1565,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionEnum, gcode_remap_x)) ((ConfigOptionEnum, gcode_remap_y)) ((ConfigOptionEnum, gcode_remap_z)) - ((ConfigOptionEnum, gcode_shear_x)) - ((ConfigOptionFloat, gcode_shear_x_angle)) - ((ConfigOptionEnum, gcode_shear_x_from)) - ((ConfigOptionEnum, gcode_shear_y)) - ((ConfigOptionFloat, gcode_shear_y_angle)) - ((ConfigOptionEnum, gcode_shear_y_from)) - ((ConfigOptionEnum, gcode_shear_z)) - ((ConfigOptionFloat, gcode_shear_z_angle)) - ((ConfigOptionEnum, gcode_shear_z_from)) - ((ConfigOptionEnum, gcode_scale_x)) - ((ConfigOptionFloat, gcode_scale_x_angle)) - ((ConfigOptionEnum, gcode_scale_y)) - ((ConfigOptionFloat, gcode_scale_y_angle)) - ((ConfigOptionEnum, gcode_scale_z)) - ((ConfigOptionFloat, gcode_scale_z_angle)) - ((ConfigOptionEnum, belt_gcode_transform_order)) ((ConfigOptionBool, gcode_back_transform)) ((ConfigOptionBool, belt_preslice_global)) ((ConfigOptionEnum, first_layer_plane)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4027a671e7..ae9223ad7f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -4541,9 +4541,10 @@ void TabPrinter::build_fff() belt_og->append_single_option_line("belt_support_z_offset_mode"); belt_og->append_single_option_line("belt_support_floor_mode"); - // Machine-frame transforms: applied to G-code after back-transform and - // gcode_remap, before per-axis origin snap. Maps Cartesian G-code into - // the printer's physical machine frame. + // Machine-frame transform: the shear (tan) + scale (1/cos) that map + // Cartesian G-code into the printer's physical machine frame are derived + // from the belt tilt angle. Only the post-slice axis remap and the expert + // decouple override are exposed here. { auto mf = page->new_optgroup(L("Machine frame transforms"), L"param_advanced"); { @@ -4554,45 +4555,15 @@ void TabPrinter::build_fff() mf->append_line(line); } { - Line line = { L("G-code shear X"), L("Shear applied to the X axis in the machine-frame stage (after back-transform and gcode_remap).") }; - line.append_option(mf->get_option("gcode_shear_x")); - line.append_option(mf->get_option("gcode_shear_x_angle")); - line.append_option(mf->get_option("gcode_shear_x_from")); + Line line = { L("Machine-frame tilt"), + L("The machine-frame shear (tan) and scale (1/cos) are derived from " + "the belt tilt angle. Enable 'Decouple' to set an independent " + "machine-frame angle when the physical gantry tilt differs from " + "the slicing rotation.") }; + line.append_option(mf->get_option("belt_frame_tilt_decouple")); + line.append_option(mf->get_option("belt_frame_tilt_angle")); mf->append_line(line); } - { - Line line = { L("G-code shear Y"), L("Shear applied to the Y axis in the machine-frame stage (after back-transform and gcode_remap).") }; - line.append_option(mf->get_option("gcode_shear_y")); - line.append_option(mf->get_option("gcode_shear_y_angle")); - line.append_option(mf->get_option("gcode_shear_y_from")); - mf->append_line(line); - } - { - Line line = { L("G-code shear Z"), L("Shear applied to the Z axis in the machine-frame stage (after back-transform and gcode_remap).") }; - line.append_option(mf->get_option("gcode_shear_z")); - line.append_option(mf->get_option("gcode_shear_z_angle")); - line.append_option(mf->get_option("gcode_shear_z_from")); - mf->append_line(line); - } - { - Line line = { L("G-code scale X"), L("Scale applied to the X axis in the machine-frame stage.") }; - line.append_option(mf->get_option("gcode_scale_x")); - line.append_option(mf->get_option("gcode_scale_x_angle")); - mf->append_line(line); - } - { - Line line = { L("G-code scale Y"), L("Scale applied to the Y axis in the machine-frame stage.") }; - line.append_option(mf->get_option("gcode_scale_y")); - line.append_option(mf->get_option("gcode_scale_y_angle")); - mf->append_line(line); - } - { - Line line = { L("G-code scale Z"), L("Scale applied to the Z axis in the machine-frame stage.") }; - line.append_option(mf->get_option("gcode_scale_z")); - line.append_option(mf->get_option("gcode_scale_z_angle")); - mf->append_line(line); - } - mf->append_single_option_line("belt_gcode_transform_order"); } option = optgroup->get_option("thumbnails"); @@ -5592,36 +5563,11 @@ void TabPrinter::toggle_options() toggle_option("belt_slice_rotation_angle", is_belt && rot_axis != BeltRotationAxis::None); toggle_option("belt_slice_rotation_global", is_belt && rot_axis != BeltRotationAxis::None); - // Machine-frame transforms: shown only in belt mode. - // Mirror the Advanced/Expert split used for mesh shear/scale. - toggle_line("gcode_shear_x", is_belt && expert_or_above); - toggle_line("gcode_shear_y", is_belt && expert_or_above); - toggle_line("gcode_shear_z", is_belt); - toggle_line("gcode_scale_x", is_belt && expert_or_above); - toggle_line("gcode_scale_y", is_belt); - toggle_line("gcode_scale_z", is_belt && expert_or_above); - toggle_line("belt_gcode_transform_order", is_belt); - - auto gsx = m_config->option>("gcode_shear_x")->value; - toggle_option("gcode_shear_x_angle", is_belt && gsx != BeltShearMode::None); - toggle_option("gcode_shear_x_from", is_belt && gsx != BeltShearMode::None); - - auto gsy = m_config->option>("gcode_shear_y")->value; - toggle_option("gcode_shear_y_angle", is_belt && gsy != BeltShearMode::None); - toggle_option("gcode_shear_y_from", is_belt && gsy != BeltShearMode::None); - - auto gsz = m_config->option>("gcode_shear_z")->value; - toggle_option("gcode_shear_z_angle", is_belt && gsz != BeltShearMode::None); - toggle_option("gcode_shear_z_from", is_belt && gsz != BeltShearMode::None); - - auto gscx = m_config->option>("gcode_scale_x")->value; - toggle_option("gcode_scale_x_angle", is_belt && gscx != BeltScaleMode::None); - - auto gscy = m_config->option>("gcode_scale_y")->value; - toggle_option("gcode_scale_y_angle", is_belt && gscy != BeltScaleMode::None); - - auto gscz = m_config->option>("gcode_scale_z")->value; - toggle_option("gcode_scale_z_angle", is_belt && gscz != BeltScaleMode::None); + // Machine-frame transform: derived from the belt tilt. Only the expert + // decouple override is exposed; its angle is enabled only when decoupled. + toggle_line("belt_frame_tilt_decouple", is_belt && expert_or_above); + toggle_option("belt_frame_tilt_angle", + is_belt && expert_or_above && m_config->opt_bool("belt_frame_tilt_decouple")); // First-layer plane: visible alongside the rest of belt-printer settings. toggle_line("first_layer_plane", is_belt);