mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-15 23:12:08 +00:00
unify frame tilt work
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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*/)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,57 +1,10 @@
|
||||
#include "MachineFrameTransform.hpp"
|
||||
#include "../BeltTransform.hpp"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1330,13 +1330,7 @@ static std::vector<std::string> 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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<BeltTransformOrder>::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>(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<BeltShearMode>::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<BeltShearMode>(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<BeltAxis>::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<BeltAxis>(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<BeltScaleMode>::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<BeltScaleMode>(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");
|
||||
|
||||
@@ -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<BeltRotationAxis>, 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<RemapAxis>, preslice_remap_x))
|
||||
((ConfigOptionEnum<RemapAxis>, preslice_remap_y))
|
||||
((ConfigOptionEnum<RemapAxis>, preslice_remap_z))
|
||||
@@ -1598,22 +1565,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_x))
|
||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_y))
|
||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_z))
|
||||
((ConfigOptionEnum<BeltShearMode>, gcode_shear_x))
|
||||
((ConfigOptionFloat, gcode_shear_x_angle))
|
||||
((ConfigOptionEnum<BeltAxis>, gcode_shear_x_from))
|
||||
((ConfigOptionEnum<BeltShearMode>, gcode_shear_y))
|
||||
((ConfigOptionFloat, gcode_shear_y_angle))
|
||||
((ConfigOptionEnum<BeltAxis>, gcode_shear_y_from))
|
||||
((ConfigOptionEnum<BeltShearMode>, gcode_shear_z))
|
||||
((ConfigOptionFloat, gcode_shear_z_angle))
|
||||
((ConfigOptionEnum<BeltAxis>, gcode_shear_z_from))
|
||||
((ConfigOptionEnum<BeltScaleMode>, gcode_scale_x))
|
||||
((ConfigOptionFloat, gcode_scale_x_angle))
|
||||
((ConfigOptionEnum<BeltScaleMode>, gcode_scale_y))
|
||||
((ConfigOptionFloat, gcode_scale_y_angle))
|
||||
((ConfigOptionEnum<BeltScaleMode>, gcode_scale_z))
|
||||
((ConfigOptionFloat, gcode_scale_z_angle))
|
||||
((ConfigOptionEnum<BeltTransformOrder>, belt_gcode_transform_order))
|
||||
((ConfigOptionBool, gcode_back_transform))
|
||||
((ConfigOptionBool, belt_preslice_global))
|
||||
((ConfigOptionEnum<FirstLayerPlaneMode>, first_layer_plane))
|
||||
|
||||
@@ -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<ConfigOptionEnum<BeltShearMode>>("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<ConfigOptionEnum<BeltShearMode>>("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<ConfigOptionEnum<BeltShearMode>>("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<ConfigOptionEnum<BeltScaleMode>>("gcode_scale_x")->value;
|
||||
toggle_option("gcode_scale_x_angle", is_belt && gscx != BeltScaleMode::None);
|
||||
|
||||
auto gscy = m_config->option<ConfigOptionEnum<BeltScaleMode>>("gcode_scale_y")->value;
|
||||
toggle_option("gcode_scale_y_angle", is_belt && gscy != BeltScaleMode::None);
|
||||
|
||||
auto gscz = m_config->option<ConfigOptionEnum<BeltScaleMode>>("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);
|
||||
|
||||
Reference in New Issue
Block a user