mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-16 15:32:09 +00:00
add global mesh transform option
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Custom Printer",
|
||||
"version": "02.03.02.61",
|
||||
"version": "02.03.02.62",
|
||||
"force_update": "0",
|
||||
"description": "My configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -84,11 +84,13 @@
|
||||
],
|
||||
"preslice_remap_y": "pos_z",
|
||||
"preslice_remap_z": "pos_y",
|
||||
"preslice_remap_global": "1",
|
||||
"printer_extruder_id": [
|
||||
"1"
|
||||
],
|
||||
"belt_origin_snap_y": "1",
|
||||
"belt_printer": "1",
|
||||
"belt_preslice_global": "1",
|
||||
"belt_shear_z": "pos_cot",
|
||||
"belt_shear_z_angle": "30",
|
||||
"build_plate_tilt_x": "45",
|
||||
|
||||
@@ -56,10 +56,45 @@ void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print)
|
||||
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());
|
||||
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);
|
||||
}
|
||||
|
||||
void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
||||
{
|
||||
// Global pre-slice mode: adjust origin using computed correction.
|
||||
// Transform the origin through the belt pipeline so that
|
||||
// back_transform(T * origin) = origin (correct machine position).
|
||||
// This replaces the bbox-based axis snap with an exact formula.
|
||||
//
|
||||
// Two flags trigger this path:
|
||||
// belt_preslice_global — full pipeline (scale * shear * remap) is global
|
||||
// preslice_remap_global — only the pre-slice remap is global
|
||||
// The XY origin adjustment uses the FULL forward transform either way,
|
||||
// because the back_transform applied during G-code emission is always the
|
||||
// inverse of the full pipeline. When only the remap is configured, both
|
||||
// flags produce identical math (T == R).
|
||||
bool use_global = m_config.belt_preslice_global.value
|
||||
|| (m_config.preslice_remap_global.value
|
||||
&& BeltTransformPipeline::has_preslice_remap(m_config));
|
||||
if (use_global && m_config.belt_printer.value) {
|
||||
auto *belt_writer = dynamic_cast<BeltGCodeWriter*>(m_writer.get());
|
||||
if (belt_writer) {
|
||||
// Clear snap — not needed with computed corrections
|
||||
for (int a = 0; a < 3; ++a)
|
||||
belt_writer->set_origin_snap(a, false, 0., 0.);
|
||||
}
|
||||
|
||||
// Adjust origin: transform through belt forward pipeline so that
|
||||
// the back-transform correctly recovers model-space positions.
|
||||
Transform3d T = BeltTransformPipeline::build_forward_transform(m_config);
|
||||
Vec2d cur_origin = this->origin();
|
||||
Vec3d origin3d(cur_origin.x(), cur_origin.y(), 0.);
|
||||
Vec3d adjusted = T.linear() * origin3d;
|
||||
this->set_origin(Vec2d(adjusted.x(), adjusted.y()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_origin_snap[0] && !m_origin_snap[1] && !m_origin_snap[2])
|
||||
return;
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ bool BeltBackTransform::init_from_config(const PrintConfig &config)
|
||||
bool has_global_shear = config.belt_shear_x_global.value ||
|
||||
config.belt_shear_y_global.value ||
|
||||
config.belt_shear_z_global.value;
|
||||
if (!has_global_shear && !BeltTransformPipeline::has_preslice_remap(config))
|
||||
bool has_preslice_global = config.belt_preslice_global.value
|
||||
|| config.preslice_remap_global.value;
|
||||
if (!has_global_shear && !has_preslice_global && !BeltTransformPipeline::has_preslice_remap(config))
|
||||
return false;
|
||||
|
||||
// Build the forward pipeline (scale * shear * pre_remap) and store its inverse.
|
||||
|
||||
@@ -1014,8 +1014,9 @@ 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",
|
||||
"preslice_remap_x", "preslice_remap_y", "preslice_remap_z",
|
||||
"preslice_remap_x", "preslice_remap_y", "preslice_remap_z", "preslice_remap_global",
|
||||
"gcode_remap_x", "gcode_remap_y", "gcode_remap_z", "gcode_back_transform",
|
||||
"belt_preslice_global",
|
||||
"belt_origin_snap_x", "belt_origin_offset_x",
|
||||
"belt_origin_snap_y", "belt_origin_offset_y",
|
||||
"belt_origin_snap_z", "belt_origin_offset_z",
|
||||
|
||||
@@ -585,9 +585,12 @@ private:
|
||||
double m_belt_global_z_offset { 0.0 };
|
||||
// Belt printer: min_z of mesh after belt shear (before Z-shift), for z_offset calc.
|
||||
double m_belt_min_z { 0.0 };
|
||||
// Belt printer: XY correction from global pre-slice mode, applied to G-code origin.
|
||||
Vec2d m_belt_global_xy_correction { Vec2d::Zero() };
|
||||
public:
|
||||
double belt_global_z_offset() const { return m_belt_global_z_offset; }
|
||||
double belt_min_z() const { return m_belt_min_z; }
|
||||
Vec2d belt_global_xy_correction() const { return m_belt_global_xy_correction; }
|
||||
private:
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "BeltTransform.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <cfloat>
|
||||
@@ -1520,8 +1521,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
// Orca: Updated for XYZ filament shrink compensation
|
||||
// Belt global mode: force each instance into its own PrintObject
|
||||
// so each gets independent layer Z values.
|
||||
bool belt_force_separate = m_config.belt_printer.value && m_config.belt_shear_z_global.value
|
||||
&& m_config.belt_shear_z.value != BeltShearMode::None;
|
||||
bool belt_force_separate = m_config.belt_printer.value && (
|
||||
(m_config.belt_shear_z_global.value && m_config.belt_shear_z.value != BeltShearMode::None)
|
||||
|| m_config.belt_preslice_global.value
|
||||
|| (m_config.preslice_remap_global.value && BeltTransformPipeline::has_preslice_remap(m_config)));
|
||||
model_object_status.print_instances = print_objects_from_model_object(*model_object, this->shrinkage_compensation(), belt_force_separate);
|
||||
std::vector<const PrintObjectStatus*> old;
|
||||
old.reserve(print_object_status_db.count(*model_object));
|
||||
@@ -1612,8 +1615,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
// min_shift across all objects, so one move affects everyone).
|
||||
if (belt_instances_shifted
|
||||
&& m_config.belt_printer.value
|
||||
&& m_config.belt_shear_z_global.value
|
||||
&& m_config.belt_shear_z.value != BeltShearMode::None) {
|
||||
&& ((m_config.belt_shear_z_global.value && m_config.belt_shear_z.value != BeltShearMode::None)
|
||||
|| m_config.belt_preslice_global.value
|
||||
|| (m_config.preslice_remap_global.value && BeltTransformPipeline::has_preslice_remap(m_config)))) {
|
||||
for (PrintObject *object : m_objects)
|
||||
update_apply_status(object->invalidate_step(posSlice));
|
||||
}
|
||||
|
||||
@@ -6156,6 +6156,16 @@ void PrintConfigDef::init_fff_params()
|
||||
"Rev mode mirrors relative to the build volume maximum. Default +Z: no change.",
|
||||
RemapAxis::PosZ);
|
||||
|
||||
def = this->add("preslice_remap_global", coBool);
|
||||
def->label = L("Global");
|
||||
def->category = L("Printable space");
|
||||
def->tooltip = L("When enabled, the pre-slice axis remap accounts for each object's bed position. "
|
||||
"Without this, the remap is applied locally around each object's center, so "
|
||||
"objects at different positions don't get a position-dependent contribution. "
|
||||
"Mirrors the per-axis 'Global' option on belt mesh shears, but for the remap.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
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);
|
||||
@@ -6169,6 +6179,15 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comSimple; // Visibility controlled by toggle_line in Tab.cpp
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("belt_preslice_global", coBool);
|
||||
def->label = L("Global mesh transforms");
|
||||
def->category = L("Printable space");
|
||||
def->tooltip = L("When enabled, pre-slice belt transforms (remap, shear, scale) account for "
|
||||
"each object's bed position, producing correct machine coordinates without "
|
||||
"relying on origin snap. Each instance gets its own PrintObject.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
auto add_belt_origin_snap = [this](const char *key_snap, const char *key_offset,
|
||||
const char *axis_label) {
|
||||
auto def = this->add(key_snap, coBool);
|
||||
|
||||
@@ -1490,10 +1490,12 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionEnum<RemapAxis>, preslice_remap_x))
|
||||
((ConfigOptionEnum<RemapAxis>, preslice_remap_y))
|
||||
((ConfigOptionEnum<RemapAxis>, preslice_remap_z))
|
||||
((ConfigOptionBool, preslice_remap_global))
|
||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_x))
|
||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_y))
|
||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_z))
|
||||
((ConfigOptionBool, gcode_back_transform))
|
||||
((ConfigOptionBool, belt_preslice_global))
|
||||
((ConfigOptionBool, belt_origin_snap_x))
|
||||
((ConfigOptionFloat, belt_origin_offset_x))
|
||||
((ConfigOptionBool, belt_origin_snap_y))
|
||||
|
||||
@@ -906,6 +906,7 @@ void PrintObject::slice()
|
||||
BOOST_LOG_TRIVIAL(warning) << "Belt global check: belt_printer=" << pcfg.belt_printer.value
|
||||
<< " belt_shear_z=" << int(pcfg.belt_shear_z.value)
|
||||
<< " belt_shear_z_global=" << pcfg.belt_shear_z_global.value
|
||||
<< " belt_preslice_global=" << pcfg.belt_preslice_global.value
|
||||
<< " object=" << this->model_object()->name;
|
||||
if (pcfg.belt_printer.value) {
|
||||
|
||||
@@ -916,37 +917,53 @@ void PrintObject::slice()
|
||||
<< " shift=(" << unscale<double>(inst_shift.x()) << ", " << unscale<double>(inst_shift.y()) << ")";
|
||||
double global_z_offset = 0.;
|
||||
|
||||
struct GAxis { BeltShearMode mode; double angle; int from; bool global; };
|
||||
GAxis gaxes[3] = {
|
||||
{ pcfg.belt_shear_x.value, pcfg.belt_shear_x_angle.value, int(pcfg.belt_shear_x_from.value), pcfg.belt_shear_x_global.value },
|
||||
{ pcfg.belt_shear_y.value, pcfg.belt_shear_y_angle.value, int(pcfg.belt_shear_y_from.value), pcfg.belt_shear_y_global.value },
|
||||
{ pcfg.belt_shear_z.value, pcfg.belt_shear_z_angle.value, int(pcfg.belt_shear_z_from.value), pcfg.belt_shear_z_global.value },
|
||||
};
|
||||
if (pcfg.belt_preslice_global.value) {
|
||||
// Global pre-slice mode: compute full correction c = (T.linear() - I) * d
|
||||
// where T is the belt forward transform and d is the bed position.
|
||||
Transform3d T = BeltTransformPipeline::build_forward_transform(pcfg);
|
||||
Vec3d d(unscale<double>(inst_shift.x()), unscale<double>(inst_shift.y()), 0.);
|
||||
Vec3d c = T.linear() * d - d;
|
||||
|
||||
// Only the Z-row shear contributes a Z offset from global mode.
|
||||
// (X/Y row shears with global would offset X/Y, not Z — not useful here.)
|
||||
// Offsets are RELATIVE: we subtract the minimum shift across all
|
||||
// PrintObjects so the lowest-positioned object stays at Z=0.
|
||||
const auto &za = gaxes[2]; // Z row
|
||||
if (za.global && za.mode != BeltShearMode::None && za.from < 2) {
|
||||
double factor = BeltTransformPipeline::compute_shear_factor(za.mode, za.angle);
|
||||
// The global Z offset accounts for the instance's position-
|
||||
// dependent shear contribution. m_belt_min_z is the minimum Z
|
||||
// of the mesh after pre_remap + shear + trafo_centered, which
|
||||
// includes the centering offset on the remapped Z axis.
|
||||
// Subtract the belt surface's centered Z position so we get
|
||||
// only the shear-induced contribution (same correction as the
|
||||
// belt_floor_z_shift fix).
|
||||
double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg)
|
||||
? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.;
|
||||
double shear_min_z = m_belt_min_z - belt_surface_z;
|
||||
Point phys = inst_shift; // already has center_offset subtracted
|
||||
double center_on_axis = (za.from == 0) ? unscale<double>(phys.x()) : unscale<double>(phys.y());
|
||||
global_z_offset += center_on_axis * factor + shear_min_z;
|
||||
global_z_offset = c.z();
|
||||
m_belt_global_xy_correction = Vec2d(c.x(), c.y());
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning) << "Belt preslice_global: correction=("
|
||||
<< c.x() << ", " << c.y() << ", " << c.z() << ")";
|
||||
} else {
|
||||
struct GAxis { BeltShearMode mode; double angle; int from; bool global; };
|
||||
GAxis gaxes[3] = {
|
||||
{ pcfg.belt_shear_x.value, pcfg.belt_shear_x_angle.value, int(pcfg.belt_shear_x_from.value), pcfg.belt_shear_x_global.value },
|
||||
{ pcfg.belt_shear_y.value, pcfg.belt_shear_y_angle.value, int(pcfg.belt_shear_y_from.value), pcfg.belt_shear_y_global.value },
|
||||
{ pcfg.belt_shear_z.value, pcfg.belt_shear_z_angle.value, int(pcfg.belt_shear_z_from.value), pcfg.belt_shear_z_global.value },
|
||||
};
|
||||
|
||||
// Only the Z-row shear contributes a Z offset from global mode.
|
||||
// (X/Y row shears with global would offset X/Y, not Z — not useful here.)
|
||||
const auto &za = gaxes[2]; // Z row
|
||||
if (za.global && za.mode != BeltShearMode::None && za.from < 2) {
|
||||
double factor = BeltTransformPipeline::compute_shear_factor(za.mode, za.angle);
|
||||
double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg)
|
||||
? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.;
|
||||
double shear_min_z = m_belt_min_z - belt_surface_z;
|
||||
Point phys = inst_shift;
|
||||
double center_on_axis = (za.from == 0) ? unscale<double>(phys.x()) : unscale<double>(phys.y());
|
||||
global_z_offset += center_on_axis * factor + shear_min_z;
|
||||
}
|
||||
|
||||
// Pre-slice remap global mode: when on, the remap accounts for the
|
||||
// instance bed position. The Z component of the correction
|
||||
// (R - I) * d shifts layer print_z so e.g. a Y↔Z swap with an
|
||||
// object at Y=50 prints at Z=50.
|
||||
if (pcfg.preslice_remap_global.value
|
||||
&& BeltTransformPipeline::has_preslice_remap(pcfg)) {
|
||||
Transform3d R = BeltTransformPipeline::build_preslice_remap(pcfg);
|
||||
Vec3d d(unscale<double>(inst_shift.x()), unscale<double>(inst_shift.y()), 0.);
|
||||
Vec3d remap_correction = R.linear() * d - d;
|
||||
global_z_offset += remap_correction.z();
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning) << "Belt global: z_offset=" << global_z_offset
|
||||
<< " za.global=" << za.global << " za.mode=" << int(za.mode) << " za.from=" << za.from
|
||||
<< " (relative to min across " << this->print()->objects().size() << " objects)";
|
||||
m_belt_global_z_offset = global_z_offset;
|
||||
if (std::abs(global_z_offset) > EPSILON) {
|
||||
|
||||
@@ -4439,6 +4439,7 @@ void TabPrinter::build_fff()
|
||||
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"));
|
||||
line.append_option(optgroup->get_option("preslice_remap_global"));
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
{
|
||||
@@ -4448,6 +4449,7 @@ void TabPrinter::build_fff()
|
||||
line.append_option(optgroup->get_option("gcode_remap_z"));
|
||||
optgroup->append_line(line);
|
||||
}
|
||||
optgroup->append_single_option_line("belt_preslice_global");
|
||||
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") };
|
||||
@@ -5328,22 +5330,29 @@ void TabPrinter::toggle_options()
|
||||
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);
|
||||
toggle_line("belt_preslice_global", show_remap);
|
||||
|
||||
bool belt_global = is_belt && m_config->opt_bool("belt_preslice_global");
|
||||
|
||||
// preslice_remap_global: superseded by belt_preslice_global
|
||||
toggle_option("preslice_remap_global", show_remap && !belt_global);
|
||||
|
||||
// Gray out angle/from sub-options when their parent shear/scale mode is None.
|
||||
// Per-axis globals are superseded when belt_preslice_global is on.
|
||||
auto sx = m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_x")->value;
|
||||
toggle_option("belt_shear_x_angle", is_belt && sx != BeltShearMode::None);
|
||||
toggle_option("belt_shear_x_from", is_belt && sx != BeltShearMode::None);
|
||||
toggle_option("belt_shear_x_global", is_belt && sx != BeltShearMode::None);
|
||||
toggle_option("belt_shear_x_global", is_belt && sx != BeltShearMode::None && !belt_global);
|
||||
|
||||
auto sy = m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_y")->value;
|
||||
toggle_option("belt_shear_y_angle", is_belt && sy != BeltShearMode::None);
|
||||
toggle_option("belt_shear_y_from", is_belt && sy != BeltShearMode::None);
|
||||
toggle_option("belt_shear_y_global", is_belt && sy != BeltShearMode::None);
|
||||
toggle_option("belt_shear_y_global", is_belt && sy != BeltShearMode::None && !belt_global);
|
||||
|
||||
auto sz = m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_z")->value;
|
||||
toggle_option("belt_shear_z_angle", is_belt && sz != BeltShearMode::None);
|
||||
toggle_option("belt_shear_z_from", is_belt && sz != BeltShearMode::None);
|
||||
toggle_option("belt_shear_z_global", is_belt && sz != BeltShearMode::None);
|
||||
toggle_option("belt_shear_z_global", is_belt && sz != BeltShearMode::None && !belt_global);
|
||||
|
||||
auto scx = m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_x")->value;
|
||||
toggle_option("belt_scale_x_angle", is_belt && scx != BeltScaleMode::None);
|
||||
@@ -5354,9 +5363,10 @@ void TabPrinter::toggle_options()
|
||||
auto scz = m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_z")->value;
|
||||
toggle_option("belt_scale_z_angle", is_belt && scz != BeltScaleMode::None);
|
||||
|
||||
toggle_option("belt_origin_offset_x", is_belt && m_config->opt_bool("belt_origin_snap_x"));
|
||||
toggle_option("belt_origin_offset_y", is_belt && m_config->opt_bool("belt_origin_snap_y"));
|
||||
toggle_option("belt_origin_offset_z", is_belt && m_config->opt_bool("belt_origin_snap_z"));
|
||||
// Origin snap is superseded by belt_preslice_global
|
||||
toggle_option("belt_origin_offset_x", is_belt && m_config->opt_bool("belt_origin_snap_x") && !belt_global);
|
||||
toggle_option("belt_origin_offset_y", is_belt && m_config->opt_bool("belt_origin_snap_y") && !belt_global);
|
||||
toggle_option("belt_origin_offset_z", is_belt && m_config->opt_bool("belt_origin_snap_z") && !belt_global);
|
||||
|
||||
toggle_line("belt_support_floor_mode", is_belt);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user