mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-27 21:02:23 +00:00
Decouple Slicing From Machine Frame Logic (#21)
* minor logic swap * first attempt, has a race condition * fixed the offset issue * found a solution, I think things work now (at least once I quash this race condition) * still chasing down race conditions * add manual shear / scale order strategy swap * tweak manual shear, fix ui uninitialization crash * fix z height / g-code desync issue * fix shear then scale cutoff planes * getting closer * fix support termination planes * fix incorrect offsets in shear-then-scale mode * test - fix overextrusion due to model/layer scale
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
{"sessionId":"597ab29b-cd70-40f4-95d9-fee98b3968ad","pid":1159657,"acquiredAt":1775936463783}
|
|
||||||
@@ -17,6 +17,7 @@ void BeltGCode::init_belt_writer(Print &print, bool is_bbl_printers)
|
|||||||
belt_writer->set_belt_angle(print.config().belt_printer_angle.value);
|
belt_writer->set_belt_angle(print.config().belt_printer_angle.value);
|
||||||
// Axis remap and build volume max are set by base GCode after init_belt_writer returns.
|
// Axis remap and build volume max are set by base GCode after init_belt_writer returns.
|
||||||
belt_writer->set_belt_back_transform(print.config());
|
belt_writer->set_belt_back_transform(print.config());
|
||||||
|
belt_writer->set_machine_frame_transform(print.config());
|
||||||
m_writer = std::move(belt_writer);
|
m_writer = std::move(belt_writer);
|
||||||
|
|
||||||
// Per-axis origin snap config.
|
// Per-axis origin snap config.
|
||||||
@@ -52,12 +53,33 @@ void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print)
|
|||||||
file.write_format("; belt_scale_y_angle = %.1f\n", print.config().belt_scale_y_angle.value);
|
file.write_format("; belt_scale_y_angle = %.1f\n", print.config().belt_scale_y_angle.value);
|
||||||
file.write_format("; belt_scale_z = %s\n", full_cfg.opt_serialize("belt_scale_z").c_str());
|
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);
|
file.write_format("; belt_scale_z_angle = %.1f\n", print.config().belt_scale_z_angle.value);
|
||||||
|
file.write_format("; belt_mesh_transform_order = %s\n", full_cfg.opt_serialize("belt_mesh_transform_order").c_str());
|
||||||
// Pre-slice remap configs
|
// Pre-slice remap configs
|
||||||
file.write_format("; preslice_remap_x = %s\n", full_cfg.opt_serialize("preslice_remap_x").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_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_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("; 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);
|
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());
|
||||||
|
file.write_format("; post_gcode_remap_x = %s\n", full_cfg.opt_serialize("post_gcode_remap_x").c_str());
|
||||||
|
file.write_format("; post_gcode_remap_y = %s\n", full_cfg.opt_serialize("post_gcode_remap_y").c_str());
|
||||||
|
file.write_format("; post_gcode_remap_z = %s\n", full_cfg.opt_serialize("post_gcode_remap_z").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
||||||
@@ -67,20 +89,31 @@ void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
|||||||
// back_transform(T * origin) = origin (correct machine position).
|
// back_transform(T * origin) = origin (correct machine position).
|
||||||
// This replaces the bbox-based axis snap with an exact formula.
|
// This replaces the bbox-based axis snap with an exact formula.
|
||||||
//
|
//
|
||||||
// Two flags trigger this path:
|
// Flags that trigger this path:
|
||||||
// belt_preslice_global — full pipeline (scale * shear * remap) is global
|
// belt_preslice_global — full pipeline (scale * shear * remap) is global
|
||||||
// preslice_remap_global — only the pre-slice remap is global
|
// preslice_remap_global — only the pre-slice remap is global
|
||||||
|
// belt_shear_z_global — Z-row shear treated as global (matches per-axis
|
||||||
|
// Z-offset added in PrintObjectSlice.cpp)
|
||||||
// The XY origin adjustment uses the FULL forward transform either way,
|
// The XY origin adjustment uses the FULL forward transform either way,
|
||||||
// because the back_transform applied during G-code emission is always the
|
// because the back_transform applied during G-code emission is always the
|
||||||
// inverse of the full pipeline. When only the remap is configured, both
|
// inverse of the full pipeline. Without pre-multiplication under
|
||||||
// flags produce identical math (T == R).
|
// ShearThenScale order with sy != 1, machine_y of bed position cy ends up
|
||||||
|
// at cy/sy instead of cy, which also leaves the object bottom off the belt
|
||||||
|
// plane.
|
||||||
bool use_global = m_config.belt_preslice_global.value
|
bool use_global = m_config.belt_preslice_global.value
|
||||||
|| (m_config.preslice_remap_global.value
|
|| (m_config.preslice_remap_global.value
|
||||||
&& BeltTransformPipeline::has_preslice_remap(m_config));
|
&& BeltTransformPipeline::has_preslice_remap(m_config))
|
||||||
|
|| (m_config.belt_shear_z_global.value
|
||||||
|
&& m_config.belt_shear_z.value != BeltShearMode::None);
|
||||||
if (use_global && m_config.belt_printer.value) {
|
if (use_global && m_config.belt_printer.value) {
|
||||||
auto *belt_writer = dynamic_cast<BeltGCodeWriter*>(m_writer.get());
|
auto *belt_writer = dynamic_cast<BeltGCodeWriter*>(m_writer.get());
|
||||||
if (belt_writer) {
|
if (belt_writer) {
|
||||||
// Clear snap — not needed with computed corrections
|
// The per-object lift (z_shift_val = max(0, -m_belt_min_z)) added by
|
||||||
|
// BeltSliceStrategy::apply_to_trafo is already compensated inside
|
||||||
|
// global_z_offset (via the shear_min_z term in PrintObjectSlice.cpp's
|
||||||
|
// preslice_global branch). Snap was previously used here for the same
|
||||||
|
// purpose, but with both active the lift gets subtracted twice. Clear
|
||||||
|
// any leftover snap state from a prior instance.
|
||||||
for (int a = 0; a < 3; ++a)
|
for (int a = 0; a < 3; ++a)
|
||||||
belt_writer->set_origin_snap(a, false, 0., 0.);
|
belt_writer->set_origin_snap(a, false, 0., 0.);
|
||||||
}
|
}
|
||||||
@@ -125,7 +158,12 @@ void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
|||||||
unscale<double>(inst_shift.y()),
|
unscale<double>(inst_shift.y()),
|
||||||
obj->belt_global_z_offset());
|
obj->belt_global_z_offset());
|
||||||
|
|
||||||
// Compute this instance's machine-space bbox min
|
// Compute this instance's bbox min in the Cartesian frame (post back_transform
|
||||||
|
// + axis_remap, before machine_frame_transform). Using to_cartesian instead of
|
||||||
|
// to_machine_coords ensures the 8 axis-aligned bbox corners coincide with the
|
||||||
|
// geometry's extreme points — a property that breaks under shear, which would
|
||||||
|
// mis-normalize non-cubic shapes (inverted cone, benchy) by their bbox-volume
|
||||||
|
// corners rather than their actual lowest geometry point.
|
||||||
BoundingBoxf3 bb = obj->model_object()->raw_bounding_box();
|
BoundingBoxf3 bb = obj->model_object()->raw_bounding_box();
|
||||||
Vec3d mn = bb.min.cast<double>(), mx = bb.max.cast<double>();
|
Vec3d mn = bb.min.cast<double>(), mx = bb.max.cast<double>();
|
||||||
Vec3d inst_min(std::numeric_limits<double>::max(),
|
Vec3d inst_min(std::numeric_limits<double>::max(),
|
||||||
@@ -135,7 +173,7 @@ void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
|||||||
Vec3d c((i & 1) ? mx.x() : mn.x(),
|
Vec3d c((i & 1) ? mx.x() : mn.x(),
|
||||||
(i & 2) ? mx.y() : mn.y(),
|
(i & 2) ? mx.y() : mn.y(),
|
||||||
(i & 4) ? mx.z() : mn.z());
|
(i & 4) ? mx.z() : mn.z());
|
||||||
Vec3d mc = belt_writer->to_machine_coords(full * c + shift);
|
Vec3d mc = belt_writer->to_cartesian(full * c + shift);
|
||||||
for (int a = 0; a < 3; ++a)
|
for (int a = 0; a < 3; ++a)
|
||||||
inst_min[a] = std::min(inst_min[a], mc[a]);
|
inst_min[a] = std::min(inst_min[a], mc[a]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ void BeltGCodeWriter::set_belt_back_transform(const PrintConfig &config)
|
|||||||
m_belt_back_transform.init_from_config(config);
|
m_belt_back_transform.init_from_config(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BeltGCodeWriter::set_machine_frame_transform(const PrintConfig &config)
|
||||||
|
{
|
||||||
|
m_machine_frame_transform.init_from_config(config);
|
||||||
|
}
|
||||||
|
|
||||||
void BeltGCodeWriter::set_origin_snap(int axis, bool enable, double offset, double bbox_min)
|
void BeltGCodeWriter::set_origin_snap(int axis, bool enable, double offset, double bbox_min)
|
||||||
{
|
{
|
||||||
if (axis >= 0 && axis < 3) {
|
if (axis >= 0 && axis < 3) {
|
||||||
@@ -43,17 +48,26 @@ void BeltGCodeWriter::set_origin_snap(int axis, bool enable, double offset, doub
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vec3d BeltGCodeWriter::to_cartesian(const Vec3d &pos) const
|
||||||
|
{
|
||||||
|
// back_transform → axis_remap, no origin_snap, no machine_frame_transform.
|
||||||
|
return apply_axis_remap(m_belt_back_transform.apply(pos));
|
||||||
|
}
|
||||||
|
|
||||||
Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const
|
Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const
|
||||||
{
|
{
|
||||||
// Step 1: Undo the shear/scale applied during slicing.
|
// Step 1+2: To Cartesian (back_transform + axis_remap).
|
||||||
Vec3d p = m_belt_back_transform.apply(pos);
|
Vec3d result = to_cartesian(pos);
|
||||||
// Step 2: Apply axis remap (uses inherited base class method).
|
// Step 3: Per-axis origin snap (computed in the Cartesian frame).
|
||||||
Vec3d result = apply_axis_remap(p);
|
|
||||||
// Step 3: Per-axis origin snap.
|
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
if (m_origin_snap[i])
|
if (m_origin_snap[i])
|
||||||
result[i] -= (m_origin_bbox_min[i] - m_origin_offset[i]);
|
result[i] -= (m_origin_bbox_min[i] - m_origin_offset[i]);
|
||||||
return result;
|
// Step 4: Machine-frame transform (gcode_shear / gcode_scale / post_gcode_remap)
|
||||||
|
// applied LAST so it acts as a global linear transform on the placed coords.
|
||||||
|
// Order matters: putting it before origin_snap would feed sheared bbox corners
|
||||||
|
// into the snap's per-object min calculation, mis-normalizing non-cubic geometries
|
||||||
|
// (the corners of the original bbox aren't extreme points of the sheared shape).
|
||||||
|
return m_machine_frame_transform.apply(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Overridden movement methods ------------------------------------------
|
// ---- Overridden movement methods ------------------------------------------
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "GCodeWriter.hpp"
|
#include "GCodeWriter.hpp"
|
||||||
#include "GCode/BeltBackTransform.hpp"
|
#include "GCode/BeltBackTransform.hpp"
|
||||||
|
#include "GCode/MachineFrameTransform.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@@ -21,8 +22,13 @@ public:
|
|||||||
void set_belt_angle(double angle_deg);
|
void set_belt_angle(double angle_deg);
|
||||||
bool is_belt_printer() const { return m_belt_angle_rad != 0.; }
|
bool is_belt_printer() const { return m_belt_angle_rad != 0.; }
|
||||||
void set_belt_back_transform(const PrintConfig &config);
|
void set_belt_back_transform(const PrintConfig &config);
|
||||||
|
void set_machine_frame_transform(const PrintConfig &config);
|
||||||
void set_origin_snap(int axis, bool enable, double offset, double bbox_min);
|
void set_origin_snap(int axis, bool enable, double offset, double bbox_min);
|
||||||
Vec3d to_machine_coords(const Vec3d &pos) const;
|
Vec3d to_machine_coords(const Vec3d &pos) const;
|
||||||
|
// back_transform + axis_remap only (no origin_snap, no machine_frame_transform).
|
||||||
|
// Used by on_set_origin for bbox computation in the Cartesian frame, where
|
||||||
|
// axis-aligned bbox corners coincide with the geometry's extreme points.
|
||||||
|
Vec3d to_cartesian(const Vec3d &pos) const;
|
||||||
|
|
||||||
// First-layer plane: when set to a non-null active evaluator, travel
|
// First-layer plane: when set to a non-null active evaluator, travel
|
||||||
// speed selection consults the plane per-move and uses
|
// speed selection consults the plane per-move and uses
|
||||||
@@ -47,7 +53,8 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
double m_belt_angle_rad = 0.;
|
double m_belt_angle_rad = 0.;
|
||||||
BeltBackTransform m_belt_back_transform;
|
BeltBackTransform m_belt_back_transform;
|
||||||
|
MachineFrameTransform m_machine_frame_transform;
|
||||||
bool m_origin_snap[3] = {false, false, false};
|
bool m_origin_snap[3] = {false, false, false};
|
||||||
double m_origin_offset[3] = {0., 0., 0.};
|
double m_origin_offset[3] = {0., 0., 0.};
|
||||||
double m_origin_bbox_min[3] = {0., 0., 0.};
|
double m_origin_bbox_min[3] = {0., 0., 0.};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "BeltSliceStrategy.hpp"
|
#include "BeltSliceStrategy.hpp"
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ BeltSliceStrategy::BeltSliceStrategy(const PrintConfig &config)
|
|||||||
{
|
{
|
||||||
m_shear = BeltTransformPipeline::build_shear_matrix(config, &m_has_shear);
|
m_shear = BeltTransformPipeline::build_shear_matrix(config, &m_has_shear);
|
||||||
m_scale = BeltTransformPipeline::build_scale_matrix(config, &m_has_scale);
|
m_scale = BeltTransformPipeline::build_scale_matrix(config, &m_has_scale);
|
||||||
|
m_order = config.belt_mesh_transform_order.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
||||||
@@ -22,10 +24,13 @@ void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
|||||||
bool has_remap,
|
bool has_remap,
|
||||||
double *out_belt_min_z) const
|
double *out_belt_min_z) const
|
||||||
{
|
{
|
||||||
// Shear + scale (belt-only; pre-slice remap is handled separately).
|
// ScaleThenShear: applied to a point, scale runs first then shear (m_shear * m_scale).
|
||||||
|
// ShearThenScale: applied to a point, shear runs first then scale (m_scale * m_shear).
|
||||||
if (m_has_shear || m_has_scale) {
|
if (m_has_shear || m_has_scale) {
|
||||||
Transform3d belt_xform = Transform3d::Identity();
|
Transform3d belt_xform = Transform3d::Identity();
|
||||||
belt_xform.linear() = m_scale * m_shear;
|
belt_xform.linear() = (m_order == BeltTransformOrder::ScaleThenShear)
|
||||||
|
? Matrix3d(m_shear * m_scale)
|
||||||
|
: Matrix3d(m_scale * m_shear);
|
||||||
trafo = belt_xform * trafo;
|
trafo = belt_xform * trafo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +52,12 @@ void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
|||||||
z_shift.matrix()(2, 3) = belt_z_shift_val;
|
z_shift.matrix()(2, 3) = belt_z_shift_val;
|
||||||
trafo = z_shift * trafo;
|
trafo = z_shift * trafo;
|
||||||
}
|
}
|
||||||
if (out_belt_min_z)
|
if (out_belt_min_z) {
|
||||||
*out_belt_min_z = (min_z != std::numeric_limits<double>::max()) ? min_z : 0.;
|
double new_val = (min_z != std::numeric_limits<double>::max()) ? min_z : 0.;
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] write m_belt_min_z tid=" << std::this_thread::get_id()
|
||||||
|
<< " target=" << out_belt_min_z << " old=" << *out_belt_min_z << " new=" << new_val;
|
||||||
|
*out_belt_min_z = new_val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,10 +34,11 @@ public:
|
|||||||
private:
|
private:
|
||||||
explicit BeltSliceStrategy(const PrintConfig &config);
|
explicit BeltSliceStrategy(const PrintConfig &config);
|
||||||
|
|
||||||
bool m_has_shear = false;
|
bool m_has_shear = false;
|
||||||
bool m_has_scale = false;
|
bool m_has_scale = false;
|
||||||
Matrix3d m_shear = Matrix3d::Identity();
|
Matrix3d m_shear = Matrix3d::Identity();
|
||||||
Matrix3d m_scale = Matrix3d::Identity();
|
Matrix3d m_scale = Matrix3d::Identity();
|
||||||
|
BeltTransformOrder m_order = BeltTransformOrder::ScaleThenShear;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
@@ -103,9 +103,15 @@ Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &co
|
|||||||
bool scale_active = false;
|
bool scale_active = false;
|
||||||
Matrix3d scale = build_scale_matrix(config, &scale_active);
|
Matrix3d scale = build_scale_matrix(config, &scale_active);
|
||||||
|
|
||||||
// Pipeline: scale * shear * pre_remap
|
// Match the mesh-side ordering selected by belt_mesh_transform_order so
|
||||||
|
// BeltBackTransform inverts the same composition that BeltSliceStrategy
|
||||||
|
// applied to the mesh.
|
||||||
|
// ScaleThenShear: applied to p, scale runs first then shear (shear * scale).
|
||||||
|
// ShearThenScale: applied to p, shear runs first then scale (scale * shear).
|
||||||
Transform3d combined = Transform3d::Identity();
|
Transform3d combined = Transform3d::Identity();
|
||||||
combined.linear() = scale * shear;
|
combined.linear() = (config.belt_mesh_transform_order.value == BeltTransformOrder::ScaleThenShear)
|
||||||
|
? Matrix3d(shear * scale)
|
||||||
|
: Matrix3d(scale * shear);
|
||||||
combined = combined * pre_remap;
|
combined = combined * pre_remap;
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
@@ -160,19 +166,30 @@ BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
|||||||
BeltTransformPipeline::BeltHeightResult result;
|
BeltTransformPipeline::BeltHeightResult result;
|
||||||
result.object_height = original_height;
|
result.object_height = original_height;
|
||||||
|
|
||||||
// Extract Z-axis shear/scale config.
|
// Extract Z-axis shear/scale + per-axis scale + transform order from config.
|
||||||
BeltShearMode z_shear_mode;
|
BeltShearMode z_shear_mode;
|
||||||
double z_shear_angle;
|
double z_shear_angle;
|
||||||
BeltScaleMode z_scale_mode;
|
BeltScaleMode z_scale_mode;
|
||||||
double z_scale_angle;
|
double z_scale_angle;
|
||||||
int z_shear_from;
|
int z_shear_from;
|
||||||
|
BeltScaleMode from_scale_mode; // scale on the shear's source axis
|
||||||
|
double from_scale_angle;
|
||||||
|
BeltTransformOrder order;
|
||||||
|
|
||||||
if constexpr (std::is_same_v<Config, PrintConfig>) {
|
if constexpr (std::is_same_v<Config, PrintConfig>) {
|
||||||
z_shear_mode = config.belt_shear_z.value;
|
z_shear_mode = config.belt_shear_z.value;
|
||||||
z_shear_angle = config.belt_shear_z_angle.value;
|
z_shear_angle = config.belt_shear_z_angle.value;
|
||||||
z_scale_mode = config.belt_scale_z.value;
|
z_scale_mode = config.belt_scale_z.value;
|
||||||
z_scale_angle = config.belt_scale_z_angle.value;
|
z_scale_angle = config.belt_scale_z_angle.value;
|
||||||
z_shear_from = int(config.belt_shear_z_from.value);
|
z_shear_from = int(config.belt_shear_z_from.value);
|
||||||
|
order = config.belt_mesh_transform_order.value;
|
||||||
|
if (z_shear_from == 0) {
|
||||||
|
from_scale_mode = config.belt_scale_x.value;
|
||||||
|
from_scale_angle = config.belt_scale_x_angle.value;
|
||||||
|
} else {
|
||||||
|
from_scale_mode = config.belt_scale_y.value;
|
||||||
|
from_scale_angle = config.belt_scale_y_angle.value;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// DynamicPrintConfig path
|
// DynamicPrintConfig path
|
||||||
auto get_shear = [&](const char *key) {
|
auto get_shear = [&](const char *key) {
|
||||||
@@ -191,11 +208,23 @@ BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
|||||||
auto *opt = config.template option<ConfigOptionEnum<BeltAxis>>(key);
|
auto *opt = config.template option<ConfigOptionEnum<BeltAxis>>(key);
|
||||||
return opt ? int(opt->value) : 1;
|
return opt ? int(opt->value) : 1;
|
||||||
};
|
};
|
||||||
|
auto get_order = [&](const char *key) {
|
||||||
|
auto *opt = config.template option<ConfigOptionEnum<BeltTransformOrder>>(key);
|
||||||
|
return opt ? opt->value : BeltTransformOrder::ScaleThenShear;
|
||||||
|
};
|
||||||
z_shear_mode = get_shear("belt_shear_z");
|
z_shear_mode = get_shear("belt_shear_z");
|
||||||
z_shear_angle = get_float("belt_shear_z_angle");
|
z_shear_angle = get_float("belt_shear_z_angle");
|
||||||
z_scale_mode = get_scale("belt_scale_z");
|
z_scale_mode = get_scale("belt_scale_z");
|
||||||
z_scale_angle = get_float("belt_scale_z_angle");
|
z_scale_angle = get_float("belt_scale_z_angle");
|
||||||
z_shear_from = get_axis("belt_shear_z_from");
|
z_shear_from = get_axis("belt_shear_z_from");
|
||||||
|
order = get_order("belt_mesh_transform_order");
|
||||||
|
if (z_shear_from == 0) {
|
||||||
|
from_scale_mode = get_scale("belt_scale_x");
|
||||||
|
from_scale_angle = get_float("belt_scale_x_angle");
|
||||||
|
} else {
|
||||||
|
from_scale_mode = get_scale("belt_scale_y");
|
||||||
|
from_scale_angle = get_float("belt_scale_y_angle");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_z_shear = z_shear_mode != BeltShearMode::None;
|
bool has_z_shear = z_shear_mode != BeltShearMode::None;
|
||||||
@@ -206,7 +235,8 @@ BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
|||||||
|
|
||||||
double shear_factor = has_z_shear
|
double shear_factor = has_z_shear
|
||||||
? BeltTransformPipeline::compute_shear_factor(z_shear_mode, z_shear_angle) : 0.;
|
? BeltTransformPipeline::compute_shear_factor(z_shear_mode, z_shear_angle) : 0.;
|
||||||
double scale_z = BeltTransformPipeline::compute_scale_factor(z_scale_mode, z_scale_angle);
|
double scale_z = BeltTransformPipeline::compute_scale_factor(z_scale_mode, z_scale_angle);
|
||||||
|
double scale_from = BeltTransformPipeline::compute_scale_factor(from_scale_mode, from_scale_angle);
|
||||||
|
|
||||||
if (has_z_shear && std::abs(shear_factor) > EPSILON) {
|
if (has_z_shear && std::abs(shear_factor) > EPSILON) {
|
||||||
int from = z_shear_from;
|
int from = z_shear_from;
|
||||||
@@ -214,14 +244,29 @@ BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
|||||||
double max_rz = std::numeric_limits<double>::lowest();
|
double max_rz = std::numeric_limits<double>::lowest();
|
||||||
for (double vz : {bb.min.z(), bb.max.z()})
|
for (double vz : {bb.min.z(), bb.max.z()})
|
||||||
for (double vs : {bb.min(from), bb.max(from)}) {
|
for (double vs : {bb.min(from), bb.max(from)}) {
|
||||||
double new_z = scale_z * (vz + shear_factor * vs);
|
// Mesh-frame new_z computed per ordering.
|
||||||
|
// scale-then-shear: Z_s = sz*Z_m + s_from*tan(α)*from_m
|
||||||
|
// shear-then-scale: Z_s = sz*(Z_m + tan(α)*from_m)
|
||||||
|
double new_z = (order == BeltTransformOrder::ScaleThenShear)
|
||||||
|
? scale_z * vz + scale_from * shear_factor * vs
|
||||||
|
: scale_z * (vz + shear_factor * vs);
|
||||||
min_rz = std::min(min_rz, new_z);
|
min_rz = std::min(min_rz, new_z);
|
||||||
max_rz = std::max(max_rz, new_z);
|
max_rz = std::max(max_rz, new_z);
|
||||||
}
|
}
|
||||||
result.object_height = max_rz - min_rz;
|
result.object_height = max_rz - min_rz;
|
||||||
result.floor_params.shear_factor = shear_factor;
|
// Effective slicer-frame slope of the belt surface (Z_m=0 line):
|
||||||
result.floor_params.from_axis = from;
|
// scale-then-shear: Z_s = tan(α) * Y_s → slope = tan(α)
|
||||||
result.floor_params.z_shift = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.);
|
// shear-then-scale: Z_s = sz/s_from * tan(α) * Y_s → slope = sz*tan(α)/s_from
|
||||||
|
// The downstream cutoff formula `Y_s = (print_z - z_shift) / slope`
|
||||||
|
// and floor_print_z(Y_s) = slope * Y_s + z_shift use this slope.
|
||||||
|
double effective_shear = (order == BeltTransformOrder::ScaleThenShear)
|
||||||
|
? shear_factor
|
||||||
|
: (std::abs(scale_from) > EPSILON
|
||||||
|
? scale_z * shear_factor / scale_from
|
||||||
|
: shear_factor);
|
||||||
|
result.floor_params.shear_factor = effective_shear;
|
||||||
|
result.floor_params.from_axis = from;
|
||||||
|
result.floor_params.z_shift = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.);
|
||||||
} else {
|
} else {
|
||||||
result.object_height = original_height * scale_z;
|
result.object_height = original_height * scale_z;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ public:
|
|||||||
// Also sets has_scale_out if non-null.
|
// Also sets has_scale_out if non-null.
|
||||||
static Matrix3d build_scale_matrix(const PrintConfig &config, bool *has_scale_out = nullptr);
|
static Matrix3d build_scale_matrix(const PrintConfig &config, bool *has_scale_out = nullptr);
|
||||||
|
|
||||||
// Combined forward transform: scale * shear * pre_remap.
|
// Combined forward transform. Shear/scale order is selected by
|
||||||
|
// belt_mesh_transform_order so the result matches what BeltSliceStrategy
|
||||||
|
// applied to the mesh (BeltBackTransform inverts this).
|
||||||
// Does NOT include the per-object Z-shift.
|
// Does NOT include the per-object Z-shift.
|
||||||
static Transform3d build_forward_transform(const PrintConfig &config);
|
static Transform3d build_forward_transform(const PrintConfig &config);
|
||||||
|
|
||||||
|
|||||||
@@ -221,6 +221,8 @@ set(lisbslic3r_sources
|
|||||||
GCode/AvoidCrossingPerimeters.hpp
|
GCode/AvoidCrossingPerimeters.hpp
|
||||||
GCode/BeltBackTransform.cpp
|
GCode/BeltBackTransform.cpp
|
||||||
GCode/BeltBackTransform.hpp
|
GCode/BeltBackTransform.hpp
|
||||||
|
GCode/MachineFrameTransform.cpp
|
||||||
|
GCode/MachineFrameTransform.hpp
|
||||||
GCode/ConflictChecker.cpp
|
GCode/ConflictChecker.cpp
|
||||||
GCode/ConflictChecker.hpp
|
GCode/ConflictChecker.hpp
|
||||||
GCode/CoolingBuffer.cpp
|
GCode/CoolingBuffer.cpp
|
||||||
|
|||||||
@@ -6462,6 +6462,20 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
auto _mm3_per_mm = path.mm3_per_mm * this->config().print_flow_ratio;
|
auto _mm3_per_mm = path.mm3_per_mm * this->config().print_flow_ratio;
|
||||||
_mm3_per_mm *= filament_flow_ratio;
|
_mm3_per_mm *= filament_flow_ratio;
|
||||||
|
|
||||||
|
// Belt printer: compensate for the volume change introduced by the mesh
|
||||||
|
// forward transform. path.mm3_per_mm is derived from slicer-frame layer
|
||||||
|
// height × line width, but a slicer-frame slab of volume V maps under the
|
||||||
|
// back-transform to a machine-frame region of volume V / |det(T)|. When
|
||||||
|
// det(T) > 1 — e.g. belt_scale_y = 1/cos(α) ≈ 1.155 at α = 30° — the
|
||||||
|
// machine slab holds less plastic than the slicer thinks it is filling
|
||||||
|
// and we over-extrude by det(T). Pure shear has det 1, so this is a
|
||||||
|
// no-op until scale factors deviate from 1.
|
||||||
|
if (m_config.belt_printer.value) {
|
||||||
|
double det = std::abs(BeltTransformPipeline::build_forward_transform(m_config).linear().determinant());
|
||||||
|
if (det > EPSILON)
|
||||||
|
_mm3_per_mm /= det;
|
||||||
|
}
|
||||||
|
|
||||||
if (path.role() == erTopSolidInfill) {
|
if (path.role() == erTopSolidInfill) {
|
||||||
_mm3_per_mm *= m_config.top_solid_infill_flow_ratio;
|
_mm3_per_mm *= m_config.top_solid_infill_flow_ratio;
|
||||||
} else if (path.role() == erBottomSurface) {
|
} else if (path.role() == erBottomSurface) {
|
||||||
|
|||||||
147
src/libslic3r/GCode/MachineFrameTransform.cpp
Normal file
147
src/libslic3r/GCode/MachineFrameTransform.cpp
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#include "MachineFrameTransform.hpp"
|
||||||
|
#include "../BeltTransform.hpp"
|
||||||
|
#include "../BoundingBox.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Build the post-gcode axis-remap transform (mirrors BeltTransformPipeline::build_preslice_remap
|
||||||
|
// but reads post_gcode_remap_* keys). Includes Rev-mode translation derived from build volume.
|
||||||
|
Transform3d build_post_gcode_remap(const PrintConfig &config)
|
||||||
|
{
|
||||||
|
Transform3d remap = Transform3d::Identity();
|
||||||
|
|
||||||
|
int rx = int(config.post_gcode_remap_x.value);
|
||||||
|
int ry = int(config.post_gcode_remap_y.value);
|
||||||
|
int rz = int(config.post_gcode_remap_z.value);
|
||||||
|
|
||||||
|
if (rx == int(RemapAxis::PosX) && ry == int(RemapAxis::PosY) && rz == int(RemapAxis::PosZ))
|
||||||
|
return remap;
|
||||||
|
|
||||||
|
auto remap_column = [](int r) -> Vec3d {
|
||||||
|
int axis = r % 3;
|
||||||
|
Vec3d col = Vec3d::Zero();
|
||||||
|
if (r < 3) col[axis] = 1.0;
|
||||||
|
else if (r < 6) col[axis] = -1.0;
|
||||||
|
else col[axis] = -1.0; // Rev: max - pos
|
||||||
|
return col;
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix3d lin;
|
||||||
|
lin.col(0) = remap_column(rx);
|
||||||
|
lin.col(1) = remap_column(ry);
|
||||||
|
lin.col(2) = remap_column(rz);
|
||||||
|
remap.linear() = lin;
|
||||||
|
|
||||||
|
if (rx >= 6 || ry >= 6 || rz >= 6) {
|
||||||
|
BoundingBoxf bbox_bed(config.printable_area.values);
|
||||||
|
Vec3d vol_max(bbox_bed.max.x(), bbox_bed.max.y(), config.printable_height.value);
|
||||||
|
Vec3d trans = Vec3d::Zero();
|
||||||
|
auto add_rev = [&](int r, int out) {
|
||||||
|
if (r >= 6) trans[out] = vol_max[r % 3];
|
||||||
|
};
|
||||||
|
add_rev(rx, 0);
|
||||||
|
add_rev(ry, 1);
|
||||||
|
add_rev(rz, 2);
|
||||||
|
remap.translation() = trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
return remap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_post_gcode_remap(const PrintConfig &config)
|
||||||
|
{
|
||||||
|
return int(config.post_gcode_remap_x.value) != int(RemapAxis::PosX) ||
|
||||||
|
int(config.post_gcode_remap_y.value) != int(RemapAxis::PosY) ||
|
||||||
|
int(config.post_gcode_remap_z.value) != int(RemapAxis::PosZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool MachineFrameTransform::init_from_config(const PrintConfig &config)
|
||||||
|
{
|
||||||
|
m_active = false;
|
||||||
|
m_transform = Transform3d::Identity();
|
||||||
|
|
||||||
|
if (!config.belt_printer.value)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Transform3d post_remap = build_post_gcode_remap(config);
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!shear_active && !scale_active && !has_post_gcode_remap(config))
|
||||||
|
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).
|
||||||
|
Transform3d combined = Transform3d::Identity();
|
||||||
|
combined.linear() = (config.belt_gcode_transform_order.value == BeltTransformOrder::ScaleThenShear)
|
||||||
|
? Matrix3d(shear * scale)
|
||||||
|
: Matrix3d(scale * shear);
|
||||||
|
combined = combined * post_remap;
|
||||||
|
|
||||||
|
if (combined.isApprox(Transform3d::Identity()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_transform = combined;
|
||||||
|
m_active = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3d MachineFrameTransform::apply(const Vec3d &pos) const
|
||||||
|
{
|
||||||
|
if (!m_active)
|
||||||
|
return pos;
|
||||||
|
return m_transform * pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
43
src/libslic3r/GCode/MachineFrameTransform.hpp
Normal file
43
src/libslic3r/GCode/MachineFrameTransform.hpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef slic3r_MachineFrameTransform_hpp_
|
||||||
|
#define slic3r_MachineFrameTransform_hpp_
|
||||||
|
|
||||||
|
#include "../libslic3r.h"
|
||||||
|
#include "../Point.hpp"
|
||||||
|
#include "../PrintConfig.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// Post-stage machine-frame transform for belt printers.
|
||||||
|
//
|
||||||
|
// Applied in BeltGCodeWriter::to_machine_coords AFTER the back-transform
|
||||||
|
// and the existing gcode_remap_* axis remap, and BEFORE per-axis origin snap.
|
||||||
|
// Maps Cartesian (axis-permuted) G-code coordinates into the printer's
|
||||||
|
// physical machine frame using a parallel set of options:
|
||||||
|
// gcode_shear_x/y/z + _angle + _from
|
||||||
|
// gcode_scale_x/y/z + _angle
|
||||||
|
// post_gcode_remap_x/y/z
|
||||||
|
//
|
||||||
|
// Composition matches the mesh-side pipeline: shear * scale * post_remap.
|
||||||
|
class MachineFrameTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MachineFrameTransform() = default;
|
||||||
|
|
||||||
|
// Initialize from belt printer config. Returns true if a non-identity
|
||||||
|
// transform was computed. Inactive when belt_printer is disabled or
|
||||||
|
// all three sub-stages are identity.
|
||||||
|
bool init_from_config(const PrintConfig &config);
|
||||||
|
|
||||||
|
// Apply the transform to a point. Returns pos unchanged if not active.
|
||||||
|
Vec3d apply(const Vec3d &pos) const;
|
||||||
|
|
||||||
|
bool is_active() const { return m_active; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_active = false;
|
||||||
|
Transform3d m_transform = Transform3d::Identity();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_MachineFrameTransform_hpp_
|
||||||
@@ -1326,8 +1326,17 @@ 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_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_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_scale_x", "belt_scale_x_angle", "belt_scale_y", "belt_scale_y_angle", "belt_scale_z", "belt_scale_z_angle",
|
||||||
|
"belt_mesh_transform_order",
|
||||||
"preslice_remap_x", "preslice_remap_y", "preslice_remap_z", "preslice_remap_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_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",
|
||||||
|
"post_gcode_remap_x", "post_gcode_remap_y", "post_gcode_remap_z",
|
||||||
"belt_preslice_global",
|
"belt_preslice_global",
|
||||||
"first_layer_plane", "first_layer_plane_offset", "first_layer_plane_thickness",
|
"first_layer_plane", "first_layer_plane_offset", "first_layer_plane_thickness",
|
||||||
"belt_origin_snap_x", "belt_origin_offset_x",
|
"belt_origin_snap_x", "belt_origin_offset_x",
|
||||||
|
|||||||
@@ -104,6 +104,15 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
"gcode_remap_x",
|
"gcode_remap_x",
|
||||||
"gcode_remap_y",
|
"gcode_remap_y",
|
||||||
"gcode_remap_z",
|
"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",
|
||||||
|
"post_gcode_remap_x", "post_gcode_remap_y", "post_gcode_remap_z",
|
||||||
"belt_origin_snap_x", "belt_origin_offset_x",
|
"belt_origin_snap_x", "belt_origin_offset_x",
|
||||||
"belt_origin_snap_y", "belt_origin_offset_y",
|
"belt_origin_snap_y", "belt_origin_offset_y",
|
||||||
"belt_origin_snap_z", "belt_origin_offset_z",
|
"belt_origin_snap_z", "belt_origin_offset_z",
|
||||||
@@ -310,6 +319,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
|| opt_key == "belt_scale_y_angle"
|
|| opt_key == "belt_scale_y_angle"
|
||||||
|| opt_key == "belt_scale_z"
|
|| opt_key == "belt_scale_z"
|
||||||
|| opt_key == "belt_scale_z_angle"
|
|| opt_key == "belt_scale_z_angle"
|
||||||
|
|| opt_key == "belt_mesh_transform_order"
|
||||||
|
|| opt_key == "belt_preslice_global"
|
||||||
|
|| opt_key == "preslice_remap_global"
|
||||||
|| opt_key == "preslice_remap_x"
|
|| opt_key == "preslice_remap_x"
|
||||||
|| opt_key == "preslice_remap_y"
|
|| opt_key == "preslice_remap_y"
|
||||||
|| opt_key == "preslice_remap_z") {
|
|| opt_key == "preslice_remap_z") {
|
||||||
|
|||||||
@@ -307,6 +307,12 @@ static t_config_enum_values s_keys_map_BeltAxis {
|
|||||||
};
|
};
|
||||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltAxis)
|
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BeltAxis)
|
||||||
|
|
||||||
|
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 {
|
static t_config_enum_values s_keys_map_BeltScaleMode {
|
||||||
{ "none", int(BeltScaleMode::None) },
|
{ "none", int(BeltScaleMode::None) },
|
||||||
{ "inv_sin", int(BeltScaleMode::InvSin) },
|
{ "inv_sin", int(BeltScaleMode::InvSin) },
|
||||||
@@ -6320,6 +6326,24 @@ void PrintConfigDef::init_fff_params()
|
|||||||
add_belt_scale_mode("belt_scale_z", "Function", BeltScaleMode::None);
|
add_belt_scale_mode("belt_scale_z", "Function", BeltScaleMode::None);
|
||||||
add_belt_scale_angle("belt_scale_z_angle", "Angle");
|
add_belt_scale_angle("belt_scale_z_angle", "Angle");
|
||||||
|
|
||||||
|
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 = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionEnum<BeltTransformOrder>(BeltTransformOrder::ScaleThenShear));
|
||||||
|
};
|
||||||
|
|
||||||
|
add_belt_transform_order("belt_mesh_transform_order", "Mesh transform order",
|
||||||
|
"Order in which the mesh shear and scale matrices are composed before slicing. "
|
||||||
|
"'Scale, then shear' applies scale first and then shear (current default). "
|
||||||
|
"'Shear, then scale' applies shear first and then scale. The g-code back-transform "
|
||||||
|
"follows the same order so that it correctly inverts the mesh transform.");
|
||||||
|
|
||||||
// G-code axis remap with sign
|
// G-code axis remap with sign
|
||||||
auto add_belt_remap = [this](const char *key, const char *label, const char *tooltip, RemapAxis 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);
|
auto def = this->add(key, coEnum);
|
||||||
@@ -6367,6 +6391,47 @@ 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);
|
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);
|
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);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
add_belt_shear_mode ("gcode_shear_x", "Function", BeltShearMode::None);
|
||||||
|
add_belt_shear_angle("gcode_shear_x_angle", "Angle");
|
||||||
|
add_belt_axis_enum ("gcode_shear_x_from", "From", "Source axis for X shear in the machine-frame stage.", BeltAxis::Z);
|
||||||
|
|
||||||
|
add_belt_shear_mode ("gcode_shear_y", "Function", BeltShearMode::None);
|
||||||
|
add_belt_shear_angle("gcode_shear_y_angle", "Angle");
|
||||||
|
add_belt_axis_enum ("gcode_shear_y_from", "From", "Source axis for Y shear in the machine-frame stage.", BeltAxis::Z);
|
||||||
|
|
||||||
|
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);
|
||||||
|
add_belt_scale_angle("gcode_scale_x_angle", "Angle");
|
||||||
|
|
||||||
|
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);
|
||||||
|
add_belt_scale_angle("gcode_scale_z_angle", "Angle");
|
||||||
|
|
||||||
|
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.");
|
||||||
|
|
||||||
|
add_belt_remap("post_gcode_remap_x", "X",
|
||||||
|
"Axis remap in the machine-frame stage. Applied AFTER gcode_remap, "
|
||||||
|
"to put coordinates into the printer's physical axis labelling. Default +X: no change.",
|
||||||
|
RemapAxis::PosX);
|
||||||
|
add_belt_remap("post_gcode_remap_y", "Y",
|
||||||
|
"Axis remap in the machine-frame stage. Applied AFTER gcode_remap, "
|
||||||
|
"to put coordinates into the printer's physical axis labelling. Default +Y: no change.",
|
||||||
|
RemapAxis::PosY);
|
||||||
|
add_belt_remap("post_gcode_remap_z", "Z",
|
||||||
|
"Axis remap in the machine-frame stage. Applied AFTER gcode_remap, "
|
||||||
|
"to put coordinates into the printer's physical axis labelling. Default +Z: no change.",
|
||||||
|
RemapAxis::PosZ);
|
||||||
|
|
||||||
def = this->add("gcode_back_transform", coBool);
|
def = this->add("gcode_back_transform", coBool);
|
||||||
def->label = L("G-code back-transform");
|
def->label = L("G-code back-transform");
|
||||||
def->category = L("Printable space");
|
def->category = L("Printable space");
|
||||||
|
|||||||
@@ -183,6 +183,15 @@ enum class BeltAxis
|
|||||||
Z = 2,
|
Z = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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
|
enum class RemapAxis
|
||||||
{
|
{
|
||||||
PosX = 0, PosY = 1, PosZ = 2,
|
PosX = 0, PosY = 1, PosZ = 2,
|
||||||
@@ -571,6 +580,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SlicingMode)
|
|||||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltShearMode)
|
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltShearMode)
|
||||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltScaleMode)
|
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltScaleMode)
|
||||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltAxis)
|
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltAxis)
|
||||||
|
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltTransformOrder)
|
||||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(RemapAxis)
|
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(RemapAxis)
|
||||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportFloorMode)
|
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportFloorMode)
|
||||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportZOffsetMode)
|
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BeltSupportZOffsetMode)
|
||||||
@@ -1535,6 +1545,26 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_x))
|
((ConfigOptionEnum<RemapAxis>, gcode_remap_x))
|
||||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_y))
|
((ConfigOptionEnum<RemapAxis>, gcode_remap_y))
|
||||||
((ConfigOptionEnum<RemapAxis>, gcode_remap_z))
|
((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_mesh_transform_order))
|
||||||
|
((ConfigOptionEnum<BeltTransformOrder>, belt_gcode_transform_order))
|
||||||
|
((ConfigOptionEnum<RemapAxis>, post_gcode_remap_x))
|
||||||
|
((ConfigOptionEnum<RemapAxis>, post_gcode_remap_y))
|
||||||
|
((ConfigOptionEnum<RemapAxis>, post_gcode_remap_z))
|
||||||
((ConfigOptionBool, gcode_back_transform))
|
((ConfigOptionBool, gcode_back_transform))
|
||||||
((ConfigOptionBool, belt_preslice_global))
|
((ConfigOptionBool, belt_preslice_global))
|
||||||
((ConfigOptionEnum<FirstLayerPlaneMode>, first_layer_plane))
|
((ConfigOptionEnum<FirstLayerPlaneMode>, first_layer_plane))
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
#include "Print.hpp"
|
#include "Print.hpp"
|
||||||
#include "BeltTransform.hpp"
|
#include "BeltTransform.hpp"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
#include "BoundingBox.hpp"
|
#include "BoundingBox.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
#include "ElephantFootCompensation.hpp"
|
#include "ElephantFootCompensation.hpp"
|
||||||
@@ -425,11 +427,15 @@ std::vector<std::set<int>> PrintObject::detect_extruder_geometric_unprintables()
|
|||||||
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
||||||
void PrintObject::make_perimeters()
|
void PrintObject::make_perimeters()
|
||||||
{
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] make_perimeters request tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
// prerequisites
|
// prerequisites
|
||||||
this->slice();
|
this->slice();
|
||||||
|
|
||||||
if (! this->set_started(posPerimeters))
|
if (! this->set_started(posPerimeters)) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] make_perimeters SKIP tid=" << std::this_thread::get_id() << " obj=" << this << " (already started/done)";
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] make_perimeters ENTER tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
|
|
||||||
m_print->set_status(15, L("Generating walls"));
|
m_print->set_status(15, L("Generating walls"));
|
||||||
BOOST_LOG_TRIVIAL(info) << "Generating walls..." << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Generating walls..." << log_memory_info();
|
||||||
@@ -527,6 +533,7 @@ void PrintObject::make_perimeters()
|
|||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] make_perimeters EXIT tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
this->set_done(posPerimeters);
|
this->set_done(posPerimeters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,7 +829,9 @@ void PrintObject::detect_overhangs_for_lift()
|
|||||||
|
|
||||||
void PrintObject::generate_support_material()
|
void PrintObject::generate_support_material()
|
||||||
{
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] generate_support_material request tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
if (this->set_started(posSupportMaterial)) {
|
if (this->set_started(posSupportMaterial)) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] generate_support_material ENTER tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
this->clear_support_layers();
|
this->clear_support_layers();
|
||||||
|
|
||||||
if(!has_support() && !m_print->get_no_check_flag()) {
|
if(!has_support() && !m_print->get_no_check_flag()) {
|
||||||
@@ -863,7 +872,10 @@ void PrintObject::generate_support_material()
|
|||||||
this->_generate_support_material();
|
this->_generate_support_material();
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
}
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] generate_support_material EXIT tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
this->set_done(posSupportMaterial);
|
this->set_done(posSupportMaterial);
|
||||||
|
} else {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] generate_support_material SKIP tid=" << std::this_thread::get_id() << " obj=" << this << " (already started/done)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
|
|
||||||
@@ -837,8 +838,12 @@ void groupingVolumesForBrim(PrintObject* object, LayerPtrs& layers, int firstLay
|
|||||||
// Resulting expolygons of layer regions are marked as Internal.
|
// Resulting expolygons of layer regions are marked as Internal.
|
||||||
void PrintObject::slice()
|
void PrintObject::slice()
|
||||||
{
|
{
|
||||||
if (! this->set_started(posSlice))
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] slice request tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
|
if (! this->set_started(posSlice)) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] slice SKIP tid=" << std::this_thread::get_id() << " obj=" << this << " (already started/done)";
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] slice ENTER tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
//BBS: add flag to reload scene for shell rendering
|
//BBS: add flag to reload scene for shell rendering
|
||||||
m_print->set_status(5, L("Slicing mesh"), PrintBase::SlicingStatus::RELOAD_SCENE);
|
m_print->set_status(5, L("Slicing mesh"), PrintBase::SlicingStatus::RELOAD_SCENE);
|
||||||
std::vector<coordf_t> layer_height_profile;
|
std::vector<coordf_t> layer_height_profile;
|
||||||
@@ -942,11 +947,25 @@ void PrintObject::slice()
|
|||||||
Vec3d d(unscale<double>(inst_shift.x()), unscale<double>(inst_shift.y()), 0.);
|
Vec3d d(unscale<double>(inst_shift.x()), unscale<double>(inst_shift.y()), 0.);
|
||||||
Vec3d c = T.linear() * d - d;
|
Vec3d c = T.linear() * d - d;
|
||||||
|
|
||||||
global_z_offset = c.z();
|
// Per-object shape contribution: BeltSliceStrategy::apply_to_trafo
|
||||||
|
// lifts the mesh by max(0, -m_belt_min_z) to keep slicer-frame Z
|
||||||
|
// above 0. Two objects at the same bed position but different
|
||||||
|
// m_belt_min_z (e.g. cube vs inverted-cone tip) otherwise end up
|
||||||
|
// at the same print_z, which causes the inverted-cone tip to
|
||||||
|
// start on the same layer as the cube's lowest sheared corner.
|
||||||
|
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;
|
||||||
|
|
||||||
|
global_z_offset = c.z() + shear_min_z;
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] write m_belt_global_xy_correction tid=" << std::this_thread::get_id()
|
||||||
|
<< " obj=" << this << " old=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y()
|
||||||
|
<< ") new=(" << c.x() << "," << c.y() << ")";
|
||||||
m_belt_global_xy_correction = Vec2d(c.x(), c.y());
|
m_belt_global_xy_correction = Vec2d(c.x(), c.y());
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Belt preslice_global: correction=("
|
BOOST_LOG_TRIVIAL(warning) << "Belt preslice_global: correction=("
|
||||||
<< c.x() << ", " << c.y() << ", " << c.z() << ")";
|
<< c.x() << ", " << c.y() << ", " << c.z() << ")"
|
||||||
|
<< " shear_min_z=" << shear_min_z << " (m_belt_min_z=" << m_belt_min_z << ")";
|
||||||
} else {
|
} else {
|
||||||
struct GAxis { BeltShearMode mode; double angle; int from; bool global; };
|
struct GAxis { BeltShearMode mode; double angle; int from; bool global; };
|
||||||
GAxis gaxes[3] = {
|
GAxis gaxes[3] = {
|
||||||
@@ -959,13 +978,27 @@ void PrintObject::slice()
|
|||||||
// (X/Y row shears with global would offset X/Y, not Z — not useful here.)
|
// (X/Y row shears with global would offset X/Y, not Z — not useful here.)
|
||||||
const auto &za = gaxes[2]; // Z row
|
const auto &za = gaxes[2]; // Z row
|
||||||
if (za.global && za.mode != BeltShearMode::None && za.from < 2) {
|
if (za.global && za.mode != BeltShearMode::None && za.from < 2) {
|
||||||
double factor = BeltTransformPipeline::compute_shear_factor(za.mode, za.angle);
|
// Use the full forward-transform correction (same formula as
|
||||||
|
// preslice_global) so the per-bed-position offset matches what
|
||||||
|
// BeltGCode::on_set_origin's T.linear() pre-multiplication
|
||||||
|
// expects after back-transform. The simple `cy*tan(α)` form
|
||||||
|
// is exact only for ScaleThenShear; under ShearThenScale with
|
||||||
|
// sy != 1 it leaves the object bottom off the belt plane by
|
||||||
|
// cy*tan(α)*(sy-1)/sy.
|
||||||
|
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;
|
||||||
double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg)
|
double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg)
|
||||||
? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.;
|
? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.;
|
||||||
double shear_min_z = m_belt_min_z - belt_surface_z;
|
double shear_min_z = m_belt_min_z - belt_surface_z;
|
||||||
Point phys = inst_shift;
|
global_z_offset += c.z() + shear_min_z;
|
||||||
double center_on_axis = (za.from == 0) ? unscale<double>(phys.x()) : unscale<double>(phys.y());
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] write m_belt_global_xy_correction tid=" << std::this_thread::get_id()
|
||||||
global_z_offset += center_on_axis * factor + shear_min_z;
|
<< " obj=" << this << " old=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y()
|
||||||
|
<< ") new=(" << c.x() << "," << c.y() << ")";
|
||||||
|
m_belt_global_xy_correction = Vec2d(c.x(), c.y());
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "Belt per-axis Z-shear-global: correction=("
|
||||||
|
<< c.x() << ", " << c.y() << ", " << c.z() << ")"
|
||||||
|
<< " shear_min_z=" << shear_min_z << " (m_belt_min_z=" << m_belt_min_z << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-slice remap global mode: when on, the remap accounts for the
|
// Pre-slice remap global mode: when on, the remap accounts for the
|
||||||
@@ -983,6 +1016,8 @@ void PrintObject::slice()
|
|||||||
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Belt global: z_offset=" << global_z_offset
|
BOOST_LOG_TRIVIAL(warning) << "Belt global: z_offset=" << global_z_offset
|
||||||
<< " (relative to min across " << this->print()->objects().size() << " objects)";
|
<< " (relative to min across " << this->print()->objects().size() << " objects)";
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] write m_belt_global_z_offset tid=" << std::this_thread::get_id()
|
||||||
|
<< " obj=" << this << " old=" << m_belt_global_z_offset << " new=" << global_z_offset;
|
||||||
m_belt_global_z_offset = global_z_offset;
|
m_belt_global_z_offset = global_z_offset;
|
||||||
if (std::abs(global_z_offset) > EPSILON) {
|
if (std::abs(global_z_offset) > EPSILON) {
|
||||||
for (Layer *layer : m_layers)
|
for (Layer *layer : m_layers)
|
||||||
@@ -1003,6 +1038,10 @@ void PrintObject::slice()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BBS
|
// BBS
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] slice EXIT tid=" << std::this_thread::get_id() << " obj=" << this
|
||||||
|
<< " layers=" << m_layers.size() << " belt_min_z=" << m_belt_min_z
|
||||||
|
<< " belt_global_z_offset=" << m_belt_global_z_offset
|
||||||
|
<< " belt_xy=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y() << ")";
|
||||||
this->set_done(posSlice);
|
this->set_done(posSlice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4491,6 +4491,7 @@ void TabPrinter::build_fff()
|
|||||||
line.append_option(optgroup->get_option("belt_scale_z_angle"));
|
line.append_option(optgroup->get_option("belt_scale_z_angle"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
}
|
}
|
||||||
|
optgroup->append_single_option_line("belt_mesh_transform_order");
|
||||||
{
|
{
|
||||||
Line line = { L("Pre-slice axis remap"),
|
Line line = { L("Pre-slice axis remap"),
|
||||||
L("Remap model axes before slicing so the slicer's coordinate system matches "
|
L("Remap model axes before slicing so the slicer's coordinate system matches "
|
||||||
@@ -4548,6 +4549,63 @@ void TabPrinter::build_fff()
|
|||||||
line.append_option(optgroup->get_option("belt_support_z_offset_mode"));
|
line.append_option(optgroup->get_option("belt_support_z_offset_mode"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
{
|
||||||
|
auto mf = page->new_optgroup(L("Machine frame transforms"), L"param_advanced");
|
||||||
|
{
|
||||||
|
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"));
|
||||||
|
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");
|
||||||
|
{
|
||||||
|
Line line = { L("Post-gcode axis remap"),
|
||||||
|
L("Axis remap in the machine-frame stage. Applied AFTER gcode_remap, "
|
||||||
|
"to put coordinates into the printer's physical axis labelling.") };
|
||||||
|
line.append_option(mf->get_option("post_gcode_remap_x"));
|
||||||
|
line.append_option(mf->get_option("post_gcode_remap_y"));
|
||||||
|
line.append_option(mf->get_option("post_gcode_remap_z"));
|
||||||
|
mf->append_line(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
option = optgroup->get_option("thumbnails");
|
option = optgroup->get_option("thumbnails");
|
||||||
option.opt.full_width = true;
|
option.opt.full_width = true;
|
||||||
optgroup->append_single_option_line(option, "printer_basic_information_advanced#g-code-thumbnails");
|
optgroup->append_single_option_line(option, "printer_basic_information_advanced#g-code-thumbnails");
|
||||||
@@ -5395,6 +5453,7 @@ void TabPrinter::toggle_options()
|
|||||||
toggle_line("belt_printer_infinite_y", is_belt);
|
toggle_line("belt_printer_infinite_y", is_belt);
|
||||||
for (auto el : {"belt_shear_x", "belt_shear_y", "belt_shear_z",
|
for (auto el : {"belt_shear_x", "belt_shear_y", "belt_shear_z",
|
||||||
"belt_scale_x", "belt_scale_y", "belt_scale_z",
|
"belt_scale_x", "belt_scale_y", "belt_scale_z",
|
||||||
|
"belt_mesh_transform_order",
|
||||||
"belt_origin_snap_x", "belt_origin_snap_y", "belt_origin_snap_z"})
|
"belt_origin_snap_x", "belt_origin_snap_y", "belt_origin_snap_z"})
|
||||||
toggle_line(el, is_belt);
|
toggle_line(el, is_belt);
|
||||||
|
|
||||||
@@ -5435,6 +5494,34 @@ void TabPrinter::toggle_options()
|
|||||||
auto scz = m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_z")->value;
|
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_scale_z_angle", is_belt && scz != BeltScaleMode::None);
|
||||||
|
|
||||||
|
// Machine-frame transforms: shown only in belt mode.
|
||||||
|
for (auto el : {"gcode_shear_x", "gcode_shear_y", "gcode_shear_z",
|
||||||
|
"gcode_scale_x", "gcode_scale_y", "gcode_scale_z",
|
||||||
|
"belt_gcode_transform_order",
|
||||||
|
"post_gcode_remap_x"})
|
||||||
|
toggle_line(el, 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);
|
||||||
|
|
||||||
// Origin snap is superseded by belt_preslice_global
|
// 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_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_y", is_belt && m_config->opt_bool("belt_origin_snap_y") && !belt_global);
|
||||||
|
|||||||
Reference in New Issue
Block a user