mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-29 20:01:26 +00:00
delete mesh transforms (#37)
* delete mesh shear, scale and refactor logger * clean up config options * reorder UI elements
This commit is contained in:
@@ -82,9 +82,9 @@
|
|||||||
"z_hop_types": [
|
"z_hop_types": [
|
||||||
"Normal Lift"
|
"Normal Lift"
|
||||||
],
|
],
|
||||||
"post_gcode_remap_x": "rev_x",
|
"gcode_remap_x": "rev_x",
|
||||||
"post_gcode_remap_y": "pos_z",
|
"gcode_remap_y": "pos_z",
|
||||||
"post_gcode_remap_z": "pos_y",
|
"gcode_remap_z": "pos_y",
|
||||||
"printer_extruder_id": [
|
"printer_extruder_id": [
|
||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
@@ -94,7 +94,6 @@
|
|||||||
"belt_slice_rotation_global": "1",
|
"belt_slice_rotation_global": "1",
|
||||||
"gcode_shear_z": "pos_tan",
|
"gcode_shear_z": "pos_tan",
|
||||||
"gcode_scale_y": "inv_cos",
|
"gcode_scale_y": "inv_cos",
|
||||||
"build_plate_tilt_x": "45",
|
|
||||||
"purge_in_prime_tower": "0",
|
"purge_in_prime_tower": "0",
|
||||||
"scan_first_layer": "0",
|
"scan_first_layer": "0",
|
||||||
"auxiliary_fan": "0"
|
"auxiliary_fan": "0"
|
||||||
|
|||||||
+10
-37
@@ -14,7 +14,6 @@ void BeltGCode::init_belt_writer(Print &print, bool is_bbl_printers)
|
|||||||
|
|
||||||
auto belt_writer = std::make_unique<BeltGCodeWriter>();
|
auto belt_writer = std::make_unique<BeltGCodeWriter>();
|
||||||
belt_writer->set_is_bbl_machine(is_bbl_printers);
|
belt_writer->set_is_bbl_machine(is_bbl_printers);
|
||||||
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());
|
belt_writer->set_machine_frame_transform(print.config());
|
||||||
@@ -34,30 +33,12 @@ void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print)
|
|||||||
if (!print.config().belt_printer.value)
|
if (!print.config().belt_printer.value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
file.write_format("; belt_printer_angle = %.1f\n", print.config().belt_printer_angle.value);
|
|
||||||
// Shear configs
|
|
||||||
const auto &full_cfg = print.full_print_config();
|
const auto &full_cfg = print.full_print_config();
|
||||||
file.write_format("; belt_shear_x = %s\n", full_cfg.opt_serialize("belt_shear_x").c_str());
|
// Slicing rotation: the belt tilt (axis + angle) and the single source of truth
|
||||||
file.write_format("; belt_shear_x_angle = %.1f\n", print.config().belt_shear_x_angle.value);
|
// for the physical tilt the G-code viewer uses to enable belt view.
|
||||||
file.write_format("; belt_shear_x_from = %s\n", full_cfg.opt_serialize("belt_shear_x_from").c_str());
|
|
||||||
file.write_format("; belt_shear_y = %s\n", full_cfg.opt_serialize("belt_shear_y").c_str());
|
|
||||||
file.write_format("; belt_shear_y_angle = %.1f\n", print.config().belt_shear_y_angle.value);
|
|
||||||
file.write_format("; belt_shear_y_from = %s\n", full_cfg.opt_serialize("belt_shear_y_from").c_str());
|
|
||||||
file.write_format("; belt_shear_z = %s\n", full_cfg.opt_serialize("belt_shear_z").c_str());
|
|
||||||
file.write_format("; belt_shear_z_angle = %.1f\n", print.config().belt_shear_z_angle.value);
|
|
||||||
file.write_format("; belt_shear_z_from = %s\n", full_cfg.opt_serialize("belt_shear_z_from").c_str());
|
|
||||||
// Scale configs
|
|
||||||
file.write_format("; belt_scale_x = %s\n", full_cfg.opt_serialize("belt_scale_x").c_str());
|
|
||||||
file.write_format("; belt_scale_x_angle = %.1f\n", print.config().belt_scale_x_angle.value);
|
|
||||||
file.write_format("; belt_scale_y = %s\n", full_cfg.opt_serialize("belt_scale_y").c_str());
|
|
||||||
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_angle = %.1f\n", print.config().belt_scale_z_angle.value);
|
|
||||||
// Slicing rotation configs
|
|
||||||
file.write_format("; belt_slice_rotation = %s\n", full_cfg.opt_serialize("belt_slice_rotation").c_str());
|
file.write_format("; belt_slice_rotation = %s\n", full_cfg.opt_serialize("belt_slice_rotation").c_str());
|
||||||
file.write_format("; belt_slice_rotation_angle = %.1f\n", print.config().belt_slice_rotation_angle.value);
|
file.write_format("; belt_slice_rotation_angle = %.1f\n", print.config().belt_slice_rotation_angle.value);
|
||||||
file.write_format("; belt_slice_rotation_global = %d\n", print.config().belt_slice_rotation_global.value ? 1 : 0);
|
file.write_format("; belt_slice_rotation_global = %d\n", print.config().belt_slice_rotation_global.value ? 1 : 0);
|
||||||
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());
|
||||||
@@ -81,9 +62,6 @@ void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print)
|
|||||||
file.write_format("; gcode_scale_z = %s\n", full_cfg.opt_serialize("gcode_scale_z").c_str());
|
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("; 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("; 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)
|
||||||
@@ -94,21 +72,16 @@ void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
|||||||
// This replaces the bbox-based axis snap with an exact formula.
|
// This replaces the bbox-based axis snap with an exact formula.
|
||||||
//
|
//
|
||||||
// Flags that trigger this path:
|
// Flags that trigger this path:
|
||||||
// belt_preslice_global — full pipeline (scale * shear * remap) is global
|
// belt_preslice_global — full pipeline (rotation * 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
|
// belt_slice_rotation_global — slicing rotation treated as global (matches
|
||||||
// Z-offset added in PrintObjectSlice.cpp)
|
// the per-instance 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. Without pre-multiplication under
|
// inverse of the full pipeline.
|
||||||
// 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)
|
|
||||||
|| (m_config.belt_slice_rotation_global.value
|
|| (m_config.belt_slice_rotation_global.value
|
||||||
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None
|
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None
|
||||||
&& std::abs(m_config.belt_slice_rotation_angle.value) > EPSILON);
|
&& std::abs(m_config.belt_slice_rotation_angle.value) > EPSILON);
|
||||||
@@ -117,10 +90,10 @@ void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift)
|
|||||||
if (belt_writer) {
|
if (belt_writer) {
|
||||||
// The per-object lift (z_shift_val = max(0, -m_belt_min_z)) added by
|
// The per-object lift (z_shift_val = max(0, -m_belt_min_z)) added by
|
||||||
// BeltSliceStrategy::apply_to_trafo is already compensated inside
|
// BeltSliceStrategy::apply_to_trafo is already compensated inside
|
||||||
// global_z_offset (via the shear_min_z term in PrintObjectSlice.cpp's
|
// global_z_offset (via the belt_z_shift term in PrintObjectSlice.cpp).
|
||||||
// preslice_global branch). Snap was previously used here for the same
|
// Snap was previously used here for the same purpose, but with both
|
||||||
// purpose, but with both active the lift gets subtracted twice. Clear
|
// active the lift gets subtracted twice. Clear any leftover snap
|
||||||
// any leftover snap state from a prior instance.
|
// 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.);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,6 @@ inline bool belt_point_on_first_layer(
|
|||||||
|
|
||||||
// ---- Belt configuration ---------------------------------------------------
|
// ---- Belt configuration ---------------------------------------------------
|
||||||
|
|
||||||
void BeltGCodeWriter::set_belt_angle(double angle_deg)
|
|
||||||
{
|
|
||||||
m_belt_angle_rad = Geometry::deg2rad(angle_deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BeltGCodeWriter::set_belt_back_transform(const PrintConfig &config)
|
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);
|
||||||
@@ -66,7 +61,7 @@ Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const
|
|||||||
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]);
|
||||||
Vec3d after_snap = result;
|
Vec3d after_snap = result;
|
||||||
// Step 4: Machine-frame transform (gcode_shear / gcode_scale / post_gcode_remap)
|
// Step 4: Machine-frame transform (gcode_shear / gcode_scale)
|
||||||
// applied LAST so it acts as a global linear transform on the placed coords.
|
// 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
|
// 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
|
// into the snap's per-object min calculation, mis-normalizing non-cubic geometries
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ public:
|
|||||||
BeltGCodeWriter() : GCodeWriter() {}
|
BeltGCodeWriter() : GCodeWriter() {}
|
||||||
|
|
||||||
// Belt configuration (axis remap is inherited from GCodeWriter)
|
// Belt configuration (axis remap is inherited from GCodeWriter)
|
||||||
void set_belt_angle(double angle_deg);
|
|
||||||
bool is_belt_printer() const { return m_belt_angle_rad != 0.; }
|
|
||||||
void set_belt_back_transform(const PrintConfig &config);
|
void set_belt_back_transform(const PrintConfig &config);
|
||||||
void set_machine_frame_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);
|
||||||
@@ -52,7 +50,6 @@ protected:
|
|||||||
std::string _travel_to_z(double z, const std::string &comment) override;
|
std::string _travel_to_z(double z, const std::string &comment) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double m_belt_angle_rad = 0.;
|
|
||||||
BeltBackTransform m_belt_back_transform;
|
BeltBackTransform m_belt_back_transform;
|
||||||
MachineFrameTransform m_machine_frame_transform;
|
MachineFrameTransform m_machine_frame_transform;
|
||||||
bool m_origin_snap[3] = {false, false, false};
|
bool m_origin_snap[3] = {false, false, false};
|
||||||
|
|||||||
@@ -17,10 +17,7 @@ std::unique_ptr<BeltSliceStrategy> BeltSliceStrategy::create(const PrintConfig &
|
|||||||
|
|
||||||
BeltSliceStrategy::BeltSliceStrategy(const PrintConfig &config)
|
BeltSliceStrategy::BeltSliceStrategy(const PrintConfig &config)
|
||||||
{
|
{
|
||||||
m_shear = BeltTransformPipeline::build_shear_matrix(config, &m_has_shear);
|
|
||||||
m_scale = BeltTransformPipeline::build_scale_matrix(config, &m_has_scale);
|
|
||||||
m_rotation = BeltTransformPipeline::build_rotation_matrix(config, &m_has_rotation);
|
m_rotation = BeltTransformPipeline::build_rotation_matrix(config, &m_has_rotation);
|
||||||
m_order = config.belt_mesh_transform_order.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
||||||
@@ -28,15 +25,10 @@ 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
|
||||||
{
|
{
|
||||||
// ScaleThenShear: applied to a point, scale runs first then shear (m_shear * m_scale).
|
// Rotation is the only mesh-side belt transform (matching build_forward_transform).
|
||||||
// ShearThenScale: applied to a point, shear runs first then scale (m_scale * m_shear).
|
if (m_has_rotation) {
|
||||||
// Rotation (if active) is applied AFTER shear/scale, matching build_forward_transform.
|
|
||||||
if (m_has_shear || m_has_scale || m_has_rotation) {
|
|
||||||
Matrix3d shear_scale = (m_order == BeltTransformOrder::ScaleThenShear)
|
|
||||||
? Matrix3d(m_shear * m_scale)
|
|
||||||
: Matrix3d(m_scale * m_shear);
|
|
||||||
Transform3d belt_xform = Transform3d::Identity();
|
Transform3d belt_xform = Transform3d::Identity();
|
||||||
belt_xform.linear() = Matrix3d(m_rotation * shear_scale);
|
belt_xform.linear() = m_rotation;
|
||||||
trafo = belt_xform * trafo;
|
trafo = belt_xform * trafo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +39,7 @@ void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
|||||||
// within the object) causes min_z to be computed against mesh-local vertex
|
// within the object) causes min_z to be computed against mesh-local vertex
|
||||||
// coordinates rather than object-space coordinates, so volumes translated
|
// coordinates rather than object-space coordinates, so volumes translated
|
||||||
// along the slicer's Z axis are silently excluded from the bound check.
|
// along the slicer's Z axis are silently excluded from the bound check.
|
||||||
if (has_remap || m_has_shear || m_has_scale || m_has_rotation) {
|
if (has_remap || m_has_rotation) {
|
||||||
// [BELT-DEBUG] Capture the incoming trafo for diagnostic logging.
|
// [BELT-DEBUG] Capture the incoming trafo for diagnostic logging.
|
||||||
// This is the slicer-frame transform AFTER belt_xform but BEFORE z_shift.
|
// This is the slicer-frame transform AFTER belt_xform but BEFORE z_shift.
|
||||||
const Transform3d trafo_pre_shift = trafo;
|
const Transform3d trafo_pre_shift = trafo;
|
||||||
@@ -65,9 +57,7 @@ void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
|||||||
ss << "(" << v.x() << "," << v.y() << "," << v.z() << ")";
|
ss << "(" << v.x() << "," << v.y() << "," << v.z() << ")";
|
||||||
return ss.str();
|
return ss.str();
|
||||||
};
|
};
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] apply_to_trafo enter"
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_to_trafo enter"
|
||||||
<< " has_shear=" << m_has_shear
|
|
||||||
<< " has_scale=" << m_has_scale
|
|
||||||
<< " has_rotation=" << m_has_rotation
|
<< " has_rotation=" << m_has_rotation
|
||||||
<< " has_remap=" << has_remap
|
<< " has_remap=" << has_remap
|
||||||
<< " trafo.linear=" << log_mat(trafo_pre_shift.linear())
|
<< " trafo.linear=" << log_mat(trafo_pre_shift.linear())
|
||||||
@@ -96,7 +86,7 @@ void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
|||||||
vol_min_z = std::min(vol_min_z, pt.z());
|
vol_min_z = std::min(vol_min_z, pt.z());
|
||||||
min_z = std::min(min_z, pt.z());
|
min_z = std::min(min_z, pt.z());
|
||||||
}
|
}
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] vol[" << vol_idx
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] vol[" << vol_idx
|
||||||
<< "] id=" << mv->id().id << " name='" << mv->name << "'"
|
<< "] id=" << mv->id().id << " name='" << mv->name << "'"
|
||||||
<< " mesh_bbox_min=" << log_vec3(mesh_min) << " mesh_bbox_max=" << log_vec3(mesh_max)
|
<< " mesh_bbox_min=" << log_vec3(mesh_min) << " mesh_bbox_max=" << log_vec3(mesh_max)
|
||||||
<< " get_matrix.translation=" << log_vec3(mv->get_matrix().translation())
|
<< " get_matrix.translation=" << log_vec3(mv->get_matrix().translation())
|
||||||
@@ -105,7 +95,7 @@ void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
|||||||
++vol_idx;
|
++vol_idx;
|
||||||
}
|
}
|
||||||
double belt_z_shift_val = (min_z < 0. && min_z != std::numeric_limits<double>::max()) ? -min_z : 0.;
|
double belt_z_shift_val = (min_z < 0. && min_z != std::numeric_limits<double>::max()) ? -min_z : 0.;
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] combined min_z=" << min_z
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] combined min_z=" << min_z
|
||||||
<< " z_shift_val=" << belt_z_shift_val;
|
<< " z_shift_val=" << belt_z_shift_val;
|
||||||
if (belt_z_shift_val > 0.) {
|
if (belt_z_shift_val > 0.) {
|
||||||
Transform3d z_shift = Transform3d::Identity();
|
Transform3d z_shift = Transform3d::Identity();
|
||||||
@@ -114,11 +104,11 @@ void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo,
|
|||||||
}
|
}
|
||||||
if (out_belt_min_z) {
|
if (out_belt_min_z) {
|
||||||
double new_val = (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) << "[BELT-DEBUG] write m_belt_min_z tid=" << std::this_thread::get_id()
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] write m_belt_min_z tid=" << std::this_thread::get_id()
|
||||||
<< " target=" << out_belt_min_z << " old=" << *out_belt_min_z << " new=" << new_val;
|
<< " target=" << out_belt_min_z << " old=" << *out_belt_min_z << " new=" << new_val;
|
||||||
*out_belt_min_z = new_val;
|
*out_belt_min_z = new_val;
|
||||||
}
|
}
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] apply_to_trafo exit"
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_to_trafo exit"
|
||||||
<< " final_trafo.linear=" << log_mat(trafo.linear())
|
<< " final_trafo.linear=" << log_mat(trafo.linear())
|
||||||
<< " final_trafo.translation=" << log_vec3(trafo.translation());
|
<< " final_trafo.translation=" << log_vec3(trafo.translation());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,17 @@ namespace Slic3r {
|
|||||||
|
|
||||||
// Belt printer pre-slice transform strategy.
|
// Belt printer pre-slice transform strategy.
|
||||||
//
|
//
|
||||||
// Encapsulates the pre-remap, shear, scale, and Z-shift transforms
|
// Encapsulates the pre-remap, rotation, and Z-shift transforms that are
|
||||||
// that are applied to model geometry before slicing on belt printers.
|
// applied to model geometry before slicing on belt printers. (Shear & scale
|
||||||
// Used by PrintObjectSlice.cpp to isolate belt-specific logic from
|
// are applied to the g-code, not the mesh.) Used by PrintObjectSlice.cpp to
|
||||||
// the slicing pipeline.
|
// isolate belt-specific logic from the slicing pipeline.
|
||||||
class BeltSliceStrategy
|
class BeltSliceStrategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Create a strategy if belt_printer is enabled; returns nullptr otherwise.
|
// Create a strategy if belt_printer is enabled; returns nullptr otherwise.
|
||||||
static std::unique_ptr<BeltSliceStrategy> create(const PrintConfig &config);
|
static std::unique_ptr<BeltSliceStrategy> create(const PrintConfig &config);
|
||||||
|
|
||||||
// Apply belt-specific transforms (shear + scale + z-shift) to the slicing trafo.
|
// Apply belt-specific transforms (rotation + z-shift) to the slicing trafo.
|
||||||
// Pre-slice remap is handled separately (standalone feature).
|
// Pre-slice remap is handled separately (standalone feature).
|
||||||
// has_remap: whether pre-slice remap was already applied (affects z-shift detection).
|
// has_remap: whether pre-slice remap was already applied (affects z-shift detection).
|
||||||
void apply_to_trafo(Transform3d &trafo,
|
void apply_to_trafo(Transform3d &trafo,
|
||||||
@@ -34,13 +34,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
explicit BeltSliceStrategy(const PrintConfig &config);
|
explicit BeltSliceStrategy(const PrintConfig &config);
|
||||||
|
|
||||||
bool m_has_shear = false;
|
|
||||||
bool m_has_scale = false;
|
|
||||||
bool m_has_rotation = false;
|
bool m_has_rotation = false;
|
||||||
Matrix3d m_shear = Matrix3d::Identity();
|
|
||||||
Matrix3d m_scale = Matrix3d::Identity();
|
|
||||||
Matrix3d m_rotation = Matrix3d::Identity();
|
Matrix3d m_rotation = Matrix3d::Identity();
|
||||||
BeltTransformOrder m_order = BeltTransformOrder::ScaleThenShear;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
+10
-158
@@ -51,50 +51,6 @@ Transform3d BeltTransformPipeline::build_preslice_remap(const PrintConfig &confi
|
|||||||
return pre_remap;
|
return pre_remap;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix3d BeltTransformPipeline::build_shear_matrix(const PrintConfig &config, bool *has_shear_out)
|
|
||||||
{
|
|
||||||
struct AxisShear { BeltShearMode mode; double angle; int from; };
|
|
||||||
AxisShear axes[3] = {
|
|
||||||
{ config.belt_shear_x.value, config.belt_shear_x_angle.value, int(config.belt_shear_x_from.value) },
|
|
||||||
{ config.belt_shear_y.value, config.belt_shear_y_angle.value, int(config.belt_shear_y_from.value) },
|
|
||||||
{ config.belt_shear_z.value, config.belt_shear_z_angle.value, int(config.belt_shear_z_from.value) },
|
|
||||||
};
|
|
||||||
|
|
||||||
Matrix3d shear = Matrix3d::Identity();
|
|
||||||
bool active = false;
|
|
||||||
for (int row = 0; row < 3; ++row) {
|
|
||||||
if (axes[row].mode != BeltShearMode::None) {
|
|
||||||
double factor = compute_shear_factor(axes[row].mode, axes[row].angle);
|
|
||||||
if (std::abs(factor) > EPSILON) {
|
|
||||||
shear(row, axes[row].from) += factor;
|
|
||||||
active = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (has_shear_out) *has_shear_out = active;
|
|
||||||
return shear;
|
|
||||||
}
|
|
||||||
|
|
||||||
Matrix3d BeltTransformPipeline::build_scale_matrix(const PrintConfig &config, bool *has_scale_out)
|
|
||||||
{
|
|
||||||
double sx = compute_scale_factor(config.belt_scale_x.value, config.belt_scale_x_angle.value);
|
|
||||||
double sy = compute_scale_factor(config.belt_scale_y.value, config.belt_scale_y_angle.value);
|
|
||||||
double sz = compute_scale_factor(config.belt_scale_z.value, config.belt_scale_z_angle.value);
|
|
||||||
|
|
||||||
bool 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;
|
|
||||||
}
|
|
||||||
if (has_scale_out) *has_scale_out = active;
|
|
||||||
return scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
Matrix3d BeltTransformPipeline::build_rotation_matrix(const PrintConfig &config, bool *has_rot_out)
|
Matrix3d BeltTransformPipeline::build_rotation_matrix(const PrintConfig &config, bool *has_rot_out)
|
||||||
{
|
{
|
||||||
BeltRotationAxis axis = config.belt_slice_rotation.value;
|
BeltRotationAxis axis = config.belt_slice_rotation.value;
|
||||||
@@ -116,26 +72,13 @@ Matrix3d BeltTransformPipeline::build_rotation_matrix(const PrintConfig &config,
|
|||||||
|
|
||||||
Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &config)
|
Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &config)
|
||||||
{
|
{
|
||||||
|
// Mesh-side belt transform: rotation applied after the pre-slice axis remap.
|
||||||
|
// (Shear & scale are a g-code-side stage, not part of the mesh transform.)
|
||||||
Transform3d pre_remap = build_preslice_remap(config);
|
Transform3d pre_remap = build_preslice_remap(config);
|
||||||
bool shear_active = false;
|
Matrix3d rot = build_rotation_matrix(config);
|
||||||
Matrix3d shear = build_shear_matrix(config, &shear_active);
|
|
||||||
bool scale_active = false;
|
|
||||||
Matrix3d scale = build_scale_matrix(config, &scale_active);
|
|
||||||
bool rot_active = false;
|
|
||||||
Matrix3d rot = build_rotation_matrix(config, &rot_active);
|
|
||||||
|
|
||||||
// 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).
|
|
||||||
// Rotation is applied AFTER shear/scale: rot * shear_scale * pre_remap.
|
|
||||||
Matrix3d shear_scale = (config.belt_mesh_transform_order.value == BeltTransformOrder::ScaleThenShear)
|
|
||||||
? Matrix3d(shear * scale)
|
|
||||||
: Matrix3d(scale * shear);
|
|
||||||
|
|
||||||
Transform3d combined = Transform3d::Identity();
|
Transform3d combined = Transform3d::Identity();
|
||||||
combined.linear() = Matrix3d(rot * shear_scale);
|
combined.linear() = rot;
|
||||||
combined = combined * pre_remap;
|
combined = combined * pre_remap;
|
||||||
return combined;
|
return combined;
|
||||||
}
|
}
|
||||||
@@ -190,120 +133,33 @@ 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 + per-axis scale + transform order + rotation from config.
|
// Extract the mesh rotation from config (the sole mesh-side belt transform).
|
||||||
BeltShearMode z_shear_mode;
|
|
||||||
double z_shear_angle;
|
|
||||||
BeltScaleMode z_scale_mode;
|
|
||||||
double z_scale_angle;
|
|
||||||
int z_shear_from;
|
|
||||||
BeltScaleMode from_scale_mode; // scale on the shear's source axis
|
|
||||||
double from_scale_angle;
|
|
||||||
BeltTransformOrder order;
|
|
||||||
BeltRotationAxis rot_axis;
|
BeltRotationAxis rot_axis;
|
||||||
double rot_angle;
|
double rot_angle;
|
||||||
|
|
||||||
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_angle = config.belt_shear_z_angle.value;
|
|
||||||
z_scale_mode = config.belt_scale_z.value;
|
|
||||||
z_scale_angle = config.belt_scale_z_angle.value;
|
|
||||||
z_shear_from = int(config.belt_shear_z_from.value);
|
|
||||||
order = config.belt_mesh_transform_order.value;
|
|
||||||
rot_axis = config.belt_slice_rotation.value;
|
rot_axis = config.belt_slice_rotation.value;
|
||||||
rot_angle = config.belt_slice_rotation_angle.value;
|
rot_angle = config.belt_slice_rotation_angle.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 *opt = config.template option<ConfigOptionEnum<BeltShearMode>>(key);
|
|
||||||
return opt ? opt->value : BeltShearMode::None;
|
|
||||||
};
|
|
||||||
auto get_scale = [&](const char *key) {
|
|
||||||
auto *opt = config.template option<ConfigOptionEnum<BeltScaleMode>>(key);
|
|
||||||
return opt ? opt->value : BeltScaleMode::None;
|
|
||||||
};
|
|
||||||
auto get_float = [&](const char *key) {
|
auto get_float = [&](const char *key) {
|
||||||
auto *opt = config.template option<ConfigOptionFloat>(key);
|
auto *opt = config.template option<ConfigOptionFloat>(key);
|
||||||
return opt ? opt->value : 45.0;
|
return opt ? opt->value : 0.0;
|
||||||
};
|
|
||||||
auto get_axis = [&](const char *key) {
|
|
||||||
auto *opt = config.template option<ConfigOptionEnum<BeltAxis>>(key);
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
auto get_rot_axis = [&](const char *key) {
|
auto get_rot_axis = [&](const char *key) {
|
||||||
auto *opt = config.template option<ConfigOptionEnum<BeltRotationAxis>>(key);
|
auto *opt = config.template option<ConfigOptionEnum<BeltRotationAxis>>(key);
|
||||||
return opt ? opt->value : BeltRotationAxis::None;
|
return opt ? opt->value : BeltRotationAxis::None;
|
||||||
};
|
};
|
||||||
z_shear_mode = get_shear("belt_shear_z");
|
|
||||||
z_shear_angle = get_float("belt_shear_z_angle");
|
|
||||||
z_scale_mode = get_scale("belt_scale_z");
|
|
||||||
z_scale_angle = get_float("belt_scale_z_angle");
|
|
||||||
z_shear_from = get_axis("belt_shear_z_from");
|
|
||||||
order = get_order("belt_mesh_transform_order");
|
|
||||||
rot_axis = get_rot_axis("belt_slice_rotation");
|
rot_axis = get_rot_axis("belt_slice_rotation");
|
||||||
rot_angle = get_float("belt_slice_rotation_angle");
|
rot_angle = get_float("belt_slice_rotation_angle");
|
||||||
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_scale = z_scale_mode != BeltScaleMode::None;
|
|
||||||
bool has_rotation = rot_axis != BeltRotationAxis::None && std::abs(rot_angle) > EPSILON;
|
bool has_rotation = rot_axis != BeltRotationAxis::None && std::abs(rot_angle) > EPSILON;
|
||||||
|
if (!has_rotation)
|
||||||
if (!has_z_shear && !has_z_scale && !has_rotation)
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
double shear_factor = has_z_shear
|
// Rotation path: sweep the 8 bbox corners through R to get the rotated height,
|
||||||
? BeltTransformPipeline::compute_shear_factor(z_shear_mode, z_shear_angle) : 0.;
|
// then derive the belt floor (the image of machine-Z = 0 under R).
|
||||||
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) {
|
|
||||||
int from = z_shear_from;
|
|
||||||
double min_rz = std::numeric_limits<double>::max();
|
|
||||||
double max_rz = std::numeric_limits<double>::lowest();
|
|
||||||
for (double vz : {bb.min.z(), bb.max.z()})
|
|
||||||
for (double vs : {bb.min(from), bb.max(from)}) {
|
|
||||||
// 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);
|
|
||||||
max_rz = std::max(max_rz, new_z);
|
|
||||||
}
|
|
||||||
result.object_height = max_rz - min_rz;
|
|
||||||
// Effective slicer-frame slope of the belt surface (Z_m=0 line):
|
|
||||||
// scale-then-shear: Z_s = tan(α) * Y_s → slope = tan(α)
|
|
||||||
// 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 if (has_rotation) {
|
|
||||||
// Rotation-only path (no Z-shear): sweep 8 bbox corners through R.
|
|
||||||
double angle_rad = Geometry::deg2rad(rot_angle);
|
double angle_rad = Geometry::deg2rad(rot_angle);
|
||||||
Vec3d unit_axis;
|
Vec3d unit_axis;
|
||||||
switch (rot_axis) {
|
switch (rot_axis) {
|
||||||
@@ -323,8 +179,7 @@ BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
|||||||
min_rz = std::min(min_rz, z);
|
min_rz = std::min(min_rz, z);
|
||||||
max_rz = std::max(max_rz, z);
|
max_rz = std::max(max_rz, z);
|
||||||
}
|
}
|
||||||
// Optional Z-scale still applies multiplicatively if both are set.
|
result.object_height = max_rz - min_rz;
|
||||||
result.object_height = (max_rz - min_rz) * (has_z_scale ? scale_z : 1.0);
|
|
||||||
|
|
||||||
// Belt floor in slicer-frame is the image of z_machine = 0 under R.
|
// Belt floor in slicer-frame is the image of z_machine = 0 under R.
|
||||||
// R(+α, X): point (·, y, 0) → (·, cos α · y, sin α · y) ⇒ z = tan(α) · y_s
|
// R(+α, X): point (·, y, 0) → (·, cos α · y, sin α · y) ⇒ z = tan(α) · y_s
|
||||||
@@ -347,9 +202,6 @@ BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
result.floor_params.z_shift = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.);
|
result.floor_params.z_shift = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.);
|
||||||
} else {
|
|
||||||
result.object_height = original_height * scale_z;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,16 @@ class ModelObject;
|
|||||||
// Shared belt-printer transform math.
|
// Shared belt-printer transform math.
|
||||||
//
|
//
|
||||||
// The pre-slice pipeline applied in PrintObjectSlice.cpp is:
|
// The pre-slice pipeline applied in PrintObjectSlice.cpp is:
|
||||||
// trafo_out = z_shift * scale * shear * pre_remap * trafo_in
|
// trafo_out = z_shift * rotation * pre_remap * trafo_in
|
||||||
//
|
//
|
||||||
// This class provides the building blocks so every call site uses the
|
// Rotation is the sole mesh-side belt transform; shear & scale are applied
|
||||||
// same implementation. z_shift is object-dependent (computed from mesh
|
// to the g-code instead (see MachineFrameTransform). This class provides the
|
||||||
// vertex bounds) and is NOT included in build_forward_transform().
|
// 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.
|
||||||
class BeltTransformPipeline
|
class BeltTransformPipeline
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -75,50 +80,53 @@ public:
|
|||||||
get_int("preslice_remap_z") != int(RemapAxis::PosZ);
|
get_int("preslice_remap_z") != int(RemapAxis::PosZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool has_shear(const PrintConfig &config)
|
|
||||||
{
|
|
||||||
return config.belt_shear_x.value != BeltShearMode::None ||
|
|
||||||
config.belt_shear_y.value != BeltShearMode::None ||
|
|
||||||
config.belt_shear_z.value != BeltShearMode::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool has_scale(const PrintConfig &config)
|
|
||||||
{
|
|
||||||
double sx = compute_scale_factor(config.belt_scale_x.value, config.belt_scale_x_angle.value);
|
|
||||||
double sy = compute_scale_factor(config.belt_scale_y.value, config.belt_scale_y_angle.value);
|
|
||||||
double sz = compute_scale_factor(config.belt_scale_z.value, config.belt_scale_z_angle.value);
|
|
||||||
return std::abs(sx - 1.) > EPSILON ||
|
|
||||||
std::abs(sy - 1.) > EPSILON ||
|
|
||||||
std::abs(sz - 1.) > EPSILON;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool has_rotation(const PrintConfig &config)
|
static bool has_rotation(const PrintConfig &config)
|
||||||
{
|
{
|
||||||
return config.belt_slice_rotation.value != BeltRotationAxis::None &&
|
return config.belt_slice_rotation.value != BeltRotationAxis::None &&
|
||||||
std::abs(config.belt_slice_rotation_angle.value) > EPSILON;
|
std::abs(config.belt_slice_rotation_angle.value) > EPSILON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Physical belt tilt derived from the slicing rotation — the single source of
|
||||||
|
// truth for bed rendering, support gravity tilt and the bed-exclusion
|
||||||
|
// projection. Returns the tilt magnitude in degrees split onto the X and Y
|
||||||
|
// build-plate tilt axes according to the rotation axis:
|
||||||
|
// rotation about X → tilt_x = angle (gantry tilts in the YZ plane)
|
||||||
|
// rotation about Y → tilt_y = angle (gantry tilts in the XZ plane)
|
||||||
|
// rotation about Z / None → no tilt (in-plane spin doesn't tilt the belt)
|
||||||
|
// The magnitude uses abs(angle) so a negative rotation still reports a positive
|
||||||
|
// physical tilt.
|
||||||
|
struct PhysicalTilt { double tilt_x_deg = 0.; double tilt_y_deg = 0.; };
|
||||||
|
|
||||||
|
static PhysicalTilt physical_tilt(BeltRotationAxis axis, double angle_deg)
|
||||||
|
{
|
||||||
|
PhysicalTilt t;
|
||||||
|
double mag = std::abs(angle_deg);
|
||||||
|
switch (axis) {
|
||||||
|
case BeltRotationAxis::X: t.tilt_x_deg = mag; break;
|
||||||
|
case BeltRotationAxis::Y: t.tilt_y_deg = mag; break;
|
||||||
|
default: break; // Z / None: no physical tilt
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PhysicalTilt physical_tilt(const PrintConfig &config)
|
||||||
|
{
|
||||||
|
return physical_tilt(config.belt_slice_rotation.value,
|
||||||
|
config.belt_slice_rotation_angle.value);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Matrix builders --------------------------------------------------
|
// ---- Matrix builders --------------------------------------------------
|
||||||
|
|
||||||
// Build the pre-slice axis remap transform (includes Rev-mode translation).
|
// Build the pre-slice axis remap transform (includes Rev-mode translation).
|
||||||
static Transform3d build_preslice_remap(const PrintConfig &config);
|
static Transform3d build_preslice_remap(const PrintConfig &config);
|
||||||
|
|
||||||
// Build the 3x3 shear matrix. Returns Identity if no shear is active.
|
|
||||||
// Also sets has_shear_out if non-null.
|
|
||||||
static Matrix3d build_shear_matrix(const PrintConfig &config, bool *has_shear_out = nullptr);
|
|
||||||
|
|
||||||
// Build the 3x3 diagonal scale matrix. Returns Identity if no scale.
|
|
||||||
// Also sets has_scale_out if non-null.
|
|
||||||
static Matrix3d build_scale_matrix(const PrintConfig &config, bool *has_scale_out = nullptr);
|
|
||||||
|
|
||||||
// Build the 3x3 rotation matrix from belt_slice_rotation* config.
|
// Build the 3x3 rotation matrix from belt_slice_rotation* config.
|
||||||
// Returns Identity if rotation axis is None or angle is ~0.
|
// Returns Identity if rotation axis is None or angle is ~0.
|
||||||
// Also sets has_rot_out if non-null.
|
// Also sets has_rot_out if non-null.
|
||||||
static Matrix3d build_rotation_matrix(const PrintConfig &config, bool *has_rot_out = nullptr);
|
static Matrix3d build_rotation_matrix(const PrintConfig &config, bool *has_rot_out = nullptr);
|
||||||
|
|
||||||
// Combined forward transform. Shear/scale order is selected by
|
// Combined forward transform (rotation * pre_remap) — the mesh-side belt
|
||||||
// belt_mesh_transform_order so the result matches what BeltSliceStrategy
|
// transform that BeltSliceStrategy applies and BeltBackTransform inverts.
|
||||||
// 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);
|
||||||
|
|
||||||
|
|||||||
@@ -85,9 +85,8 @@ FirstLayerPlane::FirstLayerPlane(const PrintConfig &config)
|
|||||||
FirstLayerPlaneMode mode = config.first_layer_plane.value;
|
FirstLayerPlaneMode mode = config.first_layer_plane.value;
|
||||||
if (mode == FirstLayerPlaneMode::Auto) {
|
if (mode == FirstLayerPlaneMode::Auto) {
|
||||||
bool belt_affine_active = config.belt_printer.value &&
|
bool belt_affine_active = config.belt_printer.value &&
|
||||||
(config.belt_shear_z.value != BeltShearMode::None ||
|
config.belt_slice_rotation.value != BeltRotationAxis::None &&
|
||||||
(config.belt_slice_rotation.value != BeltRotationAxis::None &&
|
std::abs(config.belt_slice_rotation_angle.value) > EPSILON;
|
||||||
std::abs(config.belt_slice_rotation_angle.value) > EPSILON));
|
|
||||||
mode = belt_affine_active ? FirstLayerPlaneMode::BeltAffine
|
mode = belt_affine_active ? FirstLayerPlaneMode::BeltAffine
|
||||||
: FirstLayerPlaneMode::XY;
|
: FirstLayerPlaneMode::XY;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6520,14 +6520,14 @@ 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
|
// Belt printer: compensate for any volume change introduced by the mesh
|
||||||
// forward transform. path.mm3_per_mm is derived from slicer-frame layer
|
// 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
|
// 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
|
// back-transform to a machine-frame region of volume V / |det(T)|. The
|
||||||
// det(T) > 1 — e.g. belt_scale_y = 1/cos(α) ≈ 1.155 at α = 30° — the
|
// mesh transform is now rotation ∘ pre-remap, both orthogonal, so |det(T)|
|
||||||
// machine slab holds less plastic than the slicer thinks it is filling
|
// is always 1 and this is currently a no-op; it is retained as a guard in
|
||||||
// and we over-extrude by det(T). Pure shear has det 1, so this is a
|
// case a non-orthogonal mesh transform is ever reintroduced. (Machine-frame
|
||||||
// no-op until scale factors deviate from 1.
|
// shear/scale acts on the g-code in BeltGCodeWriter, not here.)
|
||||||
if (m_config.belt_printer.value) {
|
if (m_config.belt_printer.value) {
|
||||||
double det = std::abs(BeltTransformPipeline::build_forward_transform(m_config).linear().determinant());
|
double det = std::abs(BeltTransformPipeline::build_forward_transform(m_config).linear().determinant());
|
||||||
if (det > EPSILON)
|
if (det > EPSILON)
|
||||||
|
|||||||
@@ -12,18 +12,15 @@ bool BeltBackTransform::init_from_config(const PrintConfig &config)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Require at least one active transform to proceed.
|
// Require at least one active transform to proceed.
|
||||||
bool has_global_shear = config.belt_shear_x_global.value ||
|
|
||||||
config.belt_shear_y_global.value ||
|
|
||||||
config.belt_shear_z_global.value;
|
|
||||||
bool has_global_rotation = config.belt_slice_rotation_global.value
|
bool has_global_rotation = config.belt_slice_rotation_global.value
|
||||||
&& config.belt_slice_rotation.value != BeltRotationAxis::None;
|
&& config.belt_slice_rotation.value != BeltRotationAxis::None;
|
||||||
bool has_preslice_global = config.belt_preslice_global.value
|
bool has_preslice_global = config.belt_preslice_global.value
|
||||||
|| config.preslice_remap_global.value;
|
|| config.preslice_remap_global.value;
|
||||||
if (!has_global_shear && !has_global_rotation && !has_preslice_global
|
if (!has_global_rotation && !has_preslice_global
|
||||||
&& !BeltTransformPipeline::has_preslice_remap(config))
|
&& !BeltTransformPipeline::has_preslice_remap(config))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Build the forward pipeline (scale * shear * pre_remap) and store its inverse.
|
// Build the forward pipeline (rotation * pre_remap) and store its inverse.
|
||||||
Transform3d forward = BeltTransformPipeline::build_forward_transform(config);
|
Transform3d forward = BeltTransformPipeline::build_forward_transform(config);
|
||||||
if (forward.isApprox(Transform3d::Identity()))
|
if (forward.isApprox(Transform3d::Identity()))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -3072,90 +3072,21 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Belt printer angle detection from G-code header comment.
|
// Belt printer: derive the physical tilt magnitude from the slicing-rotation
|
||||||
if (boost::starts_with(comment, " belt_printer_angle = ")) {
|
// angle header comment (used to enable the preview's belt view).
|
||||||
|
if (boost::starts_with(comment, " belt_slice_rotation_angle = ")) {
|
||||||
try {
|
try {
|
||||||
m_result.belt_printer_angle = std::stof(std::string(comment.substr(22)));
|
m_result.belt_tilt_angle = std::abs(std::stof(std::string(comment.substr(29))));
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Belt printer: parse shear configs from header comments.
|
// Belt printer: parse pre-slice axis remap from header comments.
|
||||||
{
|
{
|
||||||
auto parse_shear_mode = [](const std::string &s) -> BeltShearMode {
|
|
||||||
if (s == "pos_cot") return BeltShearMode::PosCot;
|
|
||||||
if (s == "neg_cot") return BeltShearMode::NegCot;
|
|
||||||
if (s == "pos_tan") return BeltShearMode::PosTan;
|
|
||||||
if (s == "neg_tan") return BeltShearMode::NegTan;
|
|
||||||
return BeltShearMode::None;
|
|
||||||
};
|
|
||||||
auto parse_axis = [](const std::string &s) -> BeltAxis {
|
|
||||||
if (s == "y") return BeltAxis::Y;
|
|
||||||
if (s == "z") return BeltAxis::Z;
|
|
||||||
return BeltAxis::X;
|
|
||||||
};
|
|
||||||
auto parse_scale_mode = [](const std::string &s) -> BeltScaleMode {
|
|
||||||
if (s == "inv_sin") return BeltScaleMode::InvSin;
|
|
||||||
if (s == "inv_cos") return BeltScaleMode::InvCos;
|
|
||||||
if (s == "sin") return BeltScaleMode::Sin;
|
|
||||||
if (s == "cos") return BeltScaleMode::Cos;
|
|
||||||
return BeltScaleMode::None;
|
|
||||||
};
|
|
||||||
auto trim = [](const std::string &s) -> std::string {
|
auto trim = [](const std::string &s) -> std::string {
|
||||||
size_t start = s.find_first_not_of(" \t\r\n");
|
size_t start = s.find_first_not_of(" \t\r\n");
|
||||||
size_t end = s.find_last_not_of(" \t\r\n");
|
size_t end = s.find_last_not_of(" \t\r\n");
|
||||||
return (start == std::string::npos) ? "" : s.substr(start, end - start + 1);
|
return (start == std::string::npos) ? "" : s.substr(start, end - start + 1);
|
||||||
};
|
};
|
||||||
// Shear X
|
|
||||||
if (boost::starts_with(comment, " belt_shear_x = ")) {
|
|
||||||
m_result.belt_shear_x = parse_shear_mode(trim(std::string(comment.substr(16)))); return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_shear_x_angle = ")) {
|
|
||||||
try { m_result.belt_shear_x_angle = std::stof(std::string(comment.substr(22))); } catch (...) {} return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_shear_x_from = ")) {
|
|
||||||
m_result.belt_shear_x_from = parse_axis(trim(std::string(comment.substr(21)))); return;
|
|
||||||
}
|
|
||||||
// Shear Y
|
|
||||||
if (boost::starts_with(comment, " belt_shear_y = ")) {
|
|
||||||
m_result.belt_shear_y = parse_shear_mode(trim(std::string(comment.substr(16)))); return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_shear_y_angle = ")) {
|
|
||||||
try { m_result.belt_shear_y_angle = std::stof(std::string(comment.substr(22))); } catch (...) {} return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_shear_y_from = ")) {
|
|
||||||
m_result.belt_shear_y_from = parse_axis(trim(std::string(comment.substr(21)))); return;
|
|
||||||
}
|
|
||||||
// Shear Z
|
|
||||||
if (boost::starts_with(comment, " belt_shear_z = ")) {
|
|
||||||
m_result.belt_shear_z = parse_shear_mode(trim(std::string(comment.substr(16)))); return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_shear_z_angle = ")) {
|
|
||||||
try { m_result.belt_shear_z_angle = std::stof(std::string(comment.substr(22))); } catch (...) {} return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_shear_z_from = ")) {
|
|
||||||
m_result.belt_shear_z_from = parse_axis(trim(std::string(comment.substr(21)))); return;
|
|
||||||
}
|
|
||||||
// Scale X
|
|
||||||
if (boost::starts_with(comment, " belt_scale_x = ")) {
|
|
||||||
m_result.belt_scale_x = parse_scale_mode(trim(std::string(comment.substr(16)))); return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_scale_x_angle = ")) {
|
|
||||||
try { m_result.belt_scale_x_angle = std::stof(std::string(comment.substr(22))); } catch (...) {} return;
|
|
||||||
}
|
|
||||||
// Scale Y
|
|
||||||
if (boost::starts_with(comment, " belt_scale_y = ")) {
|
|
||||||
m_result.belt_scale_y = parse_scale_mode(trim(std::string(comment.substr(16)))); return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_scale_y_angle = ")) {
|
|
||||||
try { m_result.belt_scale_y_angle = std::stof(std::string(comment.substr(22))); } catch (...) {} return;
|
|
||||||
}
|
|
||||||
// Scale Z
|
|
||||||
if (boost::starts_with(comment, " belt_scale_z = ")) {
|
|
||||||
m_result.belt_scale_z = parse_scale_mode(trim(std::string(comment.substr(16)))); return;
|
|
||||||
}
|
|
||||||
if (boost::starts_with(comment, " belt_scale_z_angle = ")) {
|
|
||||||
try { m_result.belt_scale_z_angle = std::stof(std::string(comment.substr(22))); } catch (...) {} return;
|
|
||||||
}
|
|
||||||
// Pre-slice axis remap
|
// Pre-slice axis remap
|
||||||
auto parse_remap_axis = [](const std::string &s) -> RemapAxis {
|
auto parse_remap_axis = [](const std::string &s) -> RemapAxis {
|
||||||
if (s == "pos_x") return RemapAxis::PosX;
|
if (s == "pos_x") return RemapAxis::PosX;
|
||||||
|
|||||||
@@ -245,25 +245,9 @@ class Print;
|
|||||||
bool support_traditional_timelapse{true};
|
bool support_traditional_timelapse{true};
|
||||||
float printable_height;
|
float printable_height;
|
||||||
float z_offset;
|
float z_offset;
|
||||||
// Belt printer: angle for coordinate transformation in preview.
|
// Belt printer: physical tilt magnitude (deg) parsed from the slicing-rotation
|
||||||
float belt_printer_angle{ 0.f };
|
// header comment; used to enable the preview's belt view.
|
||||||
// Belt printer: per-axis shear config.
|
float belt_tilt_angle{ 0.f };
|
||||||
BeltShearMode belt_shear_x{ BeltShearMode::None };
|
|
||||||
float belt_shear_x_angle{ 45.f };
|
|
||||||
BeltAxis belt_shear_x_from{ BeltAxis::Y };
|
|
||||||
BeltShearMode belt_shear_y{ BeltShearMode::None };
|
|
||||||
float belt_shear_y_angle{ 45.f };
|
|
||||||
BeltAxis belt_shear_y_from{ BeltAxis::Y };
|
|
||||||
BeltShearMode belt_shear_z{ BeltShearMode::None };
|
|
||||||
float belt_shear_z_angle{ 45.f };
|
|
||||||
BeltAxis belt_shear_z_from{ BeltAxis::Y };
|
|
||||||
// Belt printer: per-axis scale config.
|
|
||||||
BeltScaleMode belt_scale_x{ BeltScaleMode::None };
|
|
||||||
float belt_scale_x_angle{ 45.f };
|
|
||||||
BeltScaleMode belt_scale_y{ BeltScaleMode::None };
|
|
||||||
float belt_scale_y_angle{ 45.f };
|
|
||||||
BeltScaleMode belt_scale_z{ BeltScaleMode::None };
|
|
||||||
float belt_scale_z_angle{ 45.f };
|
|
||||||
RemapAxis preslice_remap_x{ RemapAxis::PosX };
|
RemapAxis preslice_remap_x{ RemapAxis::PosX };
|
||||||
RemapAxis preslice_remap_y{ RemapAxis::PosY };
|
RemapAxis preslice_remap_y{ RemapAxis::PosY };
|
||||||
RemapAxis preslice_remap_z{ RemapAxis::PosZ };
|
RemapAxis preslice_remap_z{ RemapAxis::PosZ };
|
||||||
@@ -332,22 +316,7 @@ class Print;
|
|||||||
optimal_assignment = other.optimal_assignment;
|
optimal_assignment = other.optimal_assignment;
|
||||||
filament_change_count_map = other.filament_change_count_map;
|
filament_change_count_map = other.filament_change_count_map;
|
||||||
initial_layer_time = other.initial_layer_time;
|
initial_layer_time = other.initial_layer_time;
|
||||||
belt_printer_angle = other.belt_printer_angle;
|
belt_tilt_angle = other.belt_tilt_angle;
|
||||||
belt_shear_x = other.belt_shear_x;
|
|
||||||
belt_shear_x_angle = other.belt_shear_x_angle;
|
|
||||||
belt_shear_x_from = other.belt_shear_x_from;
|
|
||||||
belt_shear_y = other.belt_shear_y;
|
|
||||||
belt_shear_y_angle = other.belt_shear_y_angle;
|
|
||||||
belt_shear_y_from = other.belt_shear_y_from;
|
|
||||||
belt_shear_z = other.belt_shear_z;
|
|
||||||
belt_shear_z_angle = other.belt_shear_z_angle;
|
|
||||||
belt_shear_z_from = other.belt_shear_z_from;
|
|
||||||
belt_scale_x = other.belt_scale_x;
|
|
||||||
belt_scale_x_angle = other.belt_scale_x_angle;
|
|
||||||
belt_scale_y = other.belt_scale_y;
|
|
||||||
belt_scale_y_angle = other.belt_scale_y_angle;
|
|
||||||
belt_scale_z = other.belt_scale_z;
|
|
||||||
belt_scale_z_angle = other.belt_scale_z_angle;
|
|
||||||
preslice_remap_x = other.preslice_remap_x;
|
preslice_remap_x = other.preslice_remap_x;
|
||||||
preslice_remap_y = other.preslice_remap_y;
|
preslice_remap_y = other.preslice_remap_y;
|
||||||
preslice_remap_z = other.preslice_remap_z;
|
preslice_remap_z = other.preslice_remap_z;
|
||||||
|
|||||||
@@ -6,50 +6,6 @@ namespace Slic3r {
|
|||||||
|
|
||||||
namespace {
|
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.
|
// Build the 3x3 shear matrix from gcode_shear_* keys.
|
||||||
Matrix3d build_gcode_shear_matrix(const PrintConfig &config, bool &active)
|
Matrix3d build_gcode_shear_matrix(const PrintConfig &config, bool &active)
|
||||||
{
|
{
|
||||||
@@ -94,13 +50,6 @@ Matrix3d build_gcode_scale_matrix(const PrintConfig &config, bool &active)
|
|||||||
return scale;
|
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
|
} // namespace
|
||||||
|
|
||||||
bool MachineFrameTransform::init_from_config(const PrintConfig &config)
|
bool MachineFrameTransform::init_from_config(const PrintConfig &config)
|
||||||
@@ -111,13 +60,12 @@ bool MachineFrameTransform::init_from_config(const PrintConfig &config)
|
|||||||
if (!config.belt_printer.value)
|
if (!config.belt_printer.value)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Transform3d post_remap = build_post_gcode_remap(config);
|
|
||||||
bool shear_active = false;
|
bool shear_active = false;
|
||||||
Matrix3d shear = build_gcode_shear_matrix(config, shear_active);
|
Matrix3d shear = build_gcode_shear_matrix(config, shear_active);
|
||||||
bool scale_active = false;
|
bool scale_active = false;
|
||||||
Matrix3d scale = build_gcode_scale_matrix(config, scale_active);
|
Matrix3d scale = build_gcode_scale_matrix(config, scale_active);
|
||||||
|
|
||||||
if (!shear_active && !scale_active && !has_post_gcode_remap(config))
|
if (!shear_active && !scale_active)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Compose per belt_gcode_transform_order:
|
// Compose per belt_gcode_transform_order:
|
||||||
@@ -127,7 +75,6 @@ bool MachineFrameTransform::init_from_config(const PrintConfig &config)
|
|||||||
combined.linear() = (config.belt_gcode_transform_order.value == BeltTransformOrder::ScaleThenShear)
|
combined.linear() = (config.belt_gcode_transform_order.value == BeltTransformOrder::ScaleThenShear)
|
||||||
? Matrix3d(shear * scale)
|
? Matrix3d(shear * scale)
|
||||||
: Matrix3d(scale * shear);
|
: Matrix3d(scale * shear);
|
||||||
combined = combined * post_remap;
|
|
||||||
|
|
||||||
if (combined.isApprox(Transform3d::Identity()))
|
if (combined.isApprox(Transform3d::Identity()))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ namespace Slic3r {
|
|||||||
|
|
||||||
// Post-stage machine-frame transform for belt printers.
|
// Post-stage machine-frame transform for belt printers.
|
||||||
//
|
//
|
||||||
// Applied in BeltGCodeWriter::to_machine_coords AFTER the back-transform
|
// Applied in BeltGCodeWriter::to_machine_coords AFTER the back-transform,
|
||||||
// and the existing gcode_remap_* axis remap, and BEFORE per-axis origin snap.
|
// the gcode_remap_* axis remap and per-axis origin snap. Maps Cartesian
|
||||||
// Maps Cartesian (axis-permuted) G-code coordinates into the printer's
|
// (axis-permuted) G-code coordinates into the printer's physical machine
|
||||||
// physical machine frame using a parallel set of options:
|
// frame using:
|
||||||
// gcode_shear_x/y/z + _angle + _from
|
// gcode_shear_x/y/z + _angle + _from
|
||||||
// gcode_scale_x/y/z + _angle
|
// gcode_scale_x/y/z + _angle
|
||||||
// post_gcode_remap_x/y/z
|
|
||||||
//
|
//
|
||||||
// Composition matches the mesh-side pipeline: shear * scale * post_remap.
|
// Composition follows belt_gcode_transform_order (shear * scale or scale * shear).
|
||||||
class MachineFrameTransform
|
class MachineFrameTransform
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -25,7 +24,7 @@ public:
|
|||||||
|
|
||||||
// Initialize from belt printer config. Returns true if a non-identity
|
// Initialize from belt printer config. Returns true if a non-identity
|
||||||
// transform was computed. Inactive when belt_printer is disabled or
|
// transform was computed. Inactive when belt_printer is disabled or
|
||||||
// all three sub-stages are identity.
|
// both shear and scale are identity.
|
||||||
bool init_from_config(const PrintConfig &config);
|
bool init_from_config(const PrintConfig &config);
|
||||||
|
|
||||||
// Apply the transform to a point. Returns pos unchanged if not active.
|
// Apply the transform to a point. Returns pos unchanged if not active.
|
||||||
|
|||||||
@@ -1318,12 +1318,8 @@ static std::vector<std::string> s_Preset_machine_limits_options {
|
|||||||
|
|
||||||
static std::vector<std::string> s_Preset_printer_options {
|
static std::vector<std::string> s_Preset_printer_options {
|
||||||
"printer_technology",
|
"printer_technology",
|
||||||
"printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "build_plate_tilt_x", "build_plate_tilt_y", "belt_printer", "belt_printer_angle", "belt_printer_infinite_y", "belt_shear_x", "belt_shear_x_angle", "belt_shear_x_from", "belt_shear_x_global",
|
"printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "build_plate_tilt_x", "build_plate_tilt_y", "belt_printer", "belt_printer_infinite_y",
|
||||||
"belt_shear_y", "belt_shear_y_angle", "belt_shear_y_from", "belt_shear_y_global",
|
|
||||||
"belt_shear_z", "belt_shear_z_angle", "belt_shear_z_from", "belt_shear_z_global",
|
|
||||||
"belt_scale_x", "belt_scale_x_angle", "belt_scale_y", "belt_scale_y_angle", "belt_scale_z", "belt_scale_z_angle",
|
|
||||||
"belt_slice_rotation", "belt_slice_rotation_angle", "belt_slice_rotation_global",
|
"belt_slice_rotation", "belt_slice_rotation_angle", "belt_slice_rotation_global",
|
||||||
"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_x", "gcode_shear_x_angle", "gcode_shear_x_from",
|
||||||
@@ -1333,7 +1329,6 @@ static std::vector<std::string> s_Preset_printer_options {
|
|||||||
"gcode_scale_y", "gcode_scale_y_angle",
|
"gcode_scale_y", "gcode_scale_y_angle",
|
||||||
"gcode_scale_z", "gcode_scale_z_angle",
|
"gcode_scale_z", "gcode_scale_z_angle",
|
||||||
"belt_gcode_transform_order",
|
"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",
|
||||||
|
|||||||
+10
-25
@@ -112,7 +112,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
"gcode_scale_y", "gcode_scale_y_angle",
|
"gcode_scale_y", "gcode_scale_y_angle",
|
||||||
"gcode_scale_z", "gcode_scale_z_angle",
|
"gcode_scale_z", "gcode_scale_z_angle",
|
||||||
"belt_gcode_transform_order",
|
"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",
|
||||||
@@ -301,26 +300,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
|| opt_key == "build_plate_tilt_y"
|
|| opt_key == "build_plate_tilt_y"
|
||||||
// Belt printer transform options change the mesh geometry before slicing.
|
// Belt printer transform options change the mesh geometry before slicing.
|
||||||
|| opt_key == "belt_printer"
|
|| opt_key == "belt_printer"
|
||||||
|| opt_key == "belt_printer_angle"
|
|| opt_key == "belt_slice_rotation"
|
||||||
|| opt_key == "belt_shear_x"
|
|| opt_key == "belt_slice_rotation_angle"
|
||||||
|| opt_key == "belt_shear_x_angle"
|
|| opt_key == "belt_slice_rotation_global"
|
||||||
|| opt_key == "belt_shear_x_from"
|
|
||||||
|| opt_key == "belt_shear_x_global"
|
|
||||||
|| opt_key == "belt_shear_y"
|
|
||||||
|| opt_key == "belt_shear_y_angle"
|
|
||||||
|| opt_key == "belt_shear_y_from"
|
|
||||||
|| opt_key == "belt_shear_y_global"
|
|
||||||
|| opt_key == "belt_shear_z"
|
|
||||||
|| opt_key == "belt_shear_z_angle"
|
|
||||||
|| opt_key == "belt_shear_z_from"
|
|
||||||
|| opt_key == "belt_shear_z_global"
|
|
||||||
|| opt_key == "belt_scale_x"
|
|
||||||
|| opt_key == "belt_scale_x_angle"
|
|
||||||
|| opt_key == "belt_scale_y"
|
|
||||||
|| opt_key == "belt_scale_y_angle"
|
|
||||||
|| opt_key == "belt_scale_z"
|
|
||||||
|| opt_key == "belt_scale_z_angle"
|
|
||||||
|| opt_key == "belt_mesh_transform_order"
|
|
||||||
|| opt_key == "belt_preslice_global"
|
|| opt_key == "belt_preslice_global"
|
||||||
|| opt_key == "preslice_remap_global"
|
|| opt_key == "preslice_remap_global"
|
||||||
|| opt_key == "preslice_remap_x"
|
|| opt_key == "preslice_remap_x"
|
||||||
@@ -2284,10 +2266,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
|||||||
int object_count = m_objects.size();
|
int object_count = m_objects.size();
|
||||||
std::set<PrintObject*> need_slicing_objects;
|
std::set<PrintObject*> need_slicing_objects;
|
||||||
std::set<PrintObject*> re_slicing_objects;
|
std::set<PrintObject*> re_slicing_objects;
|
||||||
// Belt global Z shear: each object needs unique layer Z values based on
|
// Belt global modes couple each object's bed position into its layer Z values,
|
||||||
// its bed position, so sharing layers between "identical" objects is wrong.
|
// so sharing layers between "identical" objects is wrong.
|
||||||
bool belt_no_share = m_config.belt_printer.value && m_config.belt_shear_z_global.value
|
bool belt_no_share = m_config.belt_printer.value &&
|
||||||
&& m_config.belt_shear_z.value != BeltShearMode::None;
|
((m_config.belt_slice_rotation_global.value
|
||||||
|
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None)
|
||||||
|
|| m_config.preslice_remap_global.value
|
||||||
|
|| m_config.belt_preslice_global.value);
|
||||||
if (!use_cache) {
|
if (!use_cache) {
|
||||||
for (int index = 0; index < object_count; index++)
|
for (int index = 0; index < object_count; index++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1537,8 +1537,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
// Belt global mode: force each instance into its own PrintObject
|
// Belt global mode: force each instance into its own PrintObject
|
||||||
// so each gets independent layer Z values.
|
// so each gets independent layer Z values.
|
||||||
bool belt_force_separate = m_config.belt_printer.value && (
|
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_slice_rotation_global.value
|
||||||
|| (m_config.belt_slice_rotation_global.value
|
|
||||||
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None
|
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None
|
||||||
&& std::abs(m_config.belt_slice_rotation_angle.value) > EPSILON)
|
&& std::abs(m_config.belt_slice_rotation_angle.value) > EPSILON)
|
||||||
|| m_config.belt_preslice_global.value
|
|| m_config.belt_preslice_global.value
|
||||||
@@ -1633,8 +1632,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
// min_shift across all objects, so one move affects everyone).
|
// min_shift across all objects, so one move affects everyone).
|
||||||
if (belt_instances_shifted
|
if (belt_instances_shifted
|
||||||
&& m_config.belt_printer.value
|
&& m_config.belt_printer.value
|
||||||
&& ((m_config.belt_shear_z_global.value && m_config.belt_shear_z.value != BeltShearMode::None)
|
&& ((m_config.belt_slice_rotation_global.value
|
||||||
|| (m_config.belt_slice_rotation_global.value
|
|
||||||
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None
|
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None
|
||||||
&& std::abs(m_config.belt_slice_rotation_angle.value) > EPSILON)
|
&& std::abs(m_config.belt_slice_rotation_angle.value) > EPSILON)
|
||||||
|| m_config.belt_preslice_global.value
|
|| m_config.belt_preslice_global.value
|
||||||
|
|||||||
+93
-146
@@ -1,5 +1,6 @@
|
|||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
#include "PrintConfigConstants.hpp"
|
#include "PrintConfigConstants.hpp"
|
||||||
|
#include "BeltTransform.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
@@ -6388,18 +6389,6 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
def = this->add("belt_printer_angle", coFloat);
|
|
||||||
def->label = L("Belt angle");
|
|
||||||
def->category = L("Printable space");
|
|
||||||
def->tooltip = L("The tilt angle of the belt surface in degrees. "
|
|
||||||
"Most belt printers use a 45-degree angle. "
|
|
||||||
"This controls the rotation applied to the slicing plane and G-code coordinates.");
|
|
||||||
def->sidetext = u8"\u00B0";
|
|
||||||
def->min = 0;
|
|
||||||
def->max = 90;
|
|
||||||
def->mode = comAdvanced;
|
|
||||||
def->set_default_value(new ConfigOptionFloat(45.));
|
|
||||||
|
|
||||||
def = this->add("belt_printer_infinite_y", coBool);
|
def = this->add("belt_printer_infinite_y", coBool);
|
||||||
def->label = L("Infinite Y axis");
|
def->label = L("Infinite Y axis");
|
||||||
def->category = L("Printable space");
|
def->category = L("Printable space");
|
||||||
@@ -6409,134 +6398,42 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(true));
|
def->set_default_value(new ConfigOptionBool(true));
|
||||||
|
|
||||||
// Per-axis shear controls for belt printer
|
// Mesh rotation applied before slicing — the sole mesh-side belt transform AND
|
||||||
auto add_belt_shear_mode = [this](const char *key, const char *label, BeltShearMode default_mode,
|
// the single source of truth for the physical belt tilt (bed rendering, support
|
||||||
ConfigOptionMode mode = comAdvanced) {
|
// gravity tilt and bed-exclusion projection all derive their angle from this).
|
||||||
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_shear_global = [this](const char *key, const char *label, bool default_val = false) {
|
|
||||||
auto def = this->add(key, coBool);
|
|
||||||
def->label = L(label);
|
|
||||||
def->category = L("Printable space");
|
|
||||||
def->tooltip = L("Apply shear in global coordinates (position-aware) rather than object-local coordinates.");
|
|
||||||
def->mode = comExpert;
|
|
||||||
def->set_default_value(new ConfigOptionBool(default_val));
|
|
||||||
};
|
|
||||||
|
|
||||||
add_belt_shear_mode ("belt_shear_x", "Function", BeltShearMode::None, comExpert);
|
|
||||||
add_belt_shear_angle("belt_shear_x_angle", "Angle", comExpert);
|
|
||||||
add_belt_axis_enum ("belt_shear_x_from", "From", "Source axis for X shear.", BeltAxis::Z, comExpert);
|
|
||||||
add_belt_shear_global("belt_shear_x_global", "Global");
|
|
||||||
|
|
||||||
add_belt_shear_mode ("belt_shear_y", "Function", BeltShearMode::None, comExpert);
|
|
||||||
add_belt_shear_angle("belt_shear_y_angle", "Angle", comExpert);
|
|
||||||
add_belt_axis_enum ("belt_shear_y_from", "From", "Source axis for Y shear.", BeltAxis::Z, comExpert);
|
|
||||||
add_belt_shear_global("belt_shear_y_global", "Global");
|
|
||||||
|
|
||||||
add_belt_shear_mode ("belt_shear_z", "Function", BeltShearMode::None, comExpert);
|
|
||||||
add_belt_shear_angle("belt_shear_z_angle", "Angle", comExpert);
|
|
||||||
add_belt_axis_enum ("belt_shear_z_from", "From", "Source axis for Z shear.", BeltAxis::Y, comExpert);
|
|
||||||
add_belt_shear_global("belt_shear_z_global", "Global", true);
|
|
||||||
|
|
||||||
// Per-axis scale controls for belt printer
|
|
||||||
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_scale_mode ("belt_scale_x", "Function", BeltScaleMode::None, comExpert);
|
|
||||||
add_belt_scale_angle("belt_scale_x_angle", "Angle", comExpert);
|
|
||||||
|
|
||||||
add_belt_scale_mode ("belt_scale_y", "Function", BeltScaleMode::None, comExpert);
|
|
||||||
add_belt_scale_angle("belt_scale_y_angle", "Angle", comExpert);
|
|
||||||
|
|
||||||
add_belt_scale_mode ("belt_scale_z", "Function", BeltScaleMode::None, comExpert);
|
|
||||||
add_belt_scale_angle("belt_scale_z_angle", "Angle", comExpert);
|
|
||||||
|
|
||||||
// Global slicing rotation (alternative to per-axis shear+scale).
|
|
||||||
def = this->add("belt_slice_rotation", coEnum);
|
def = this->add("belt_slice_rotation", coEnum);
|
||||||
def->label = L("Slicing rotation axis");
|
def->label = L("Belt tilt axis");
|
||||||
def->category = L("Printable space");
|
def->category = L("Printable space");
|
||||||
def->tooltip = L("Rotate the mesh by this axis before slicing. Use this for an "
|
def->tooltip = L("Axis the mesh is rotated about before slicing. This is the belt "
|
||||||
"isometric (no shear distortion) belt slicing transform. "
|
"printer's tilt: an isometric (no distortion) rotation that also "
|
||||||
"Mutually exclusive with the per-axis shear/scale controls "
|
"drives bed rendering and support gravity tilt, and that the g-code "
|
||||||
"in the UI; the pipeline composes them if both are set in JSON.");
|
"back-transform inverts before the machine-frame shear/scale and remap. "
|
||||||
|
"X is the typical gantry tilt (belt travels along Y).");
|
||||||
def->enum_keys_map = &ConfigOptionEnum<BeltRotationAxis>::get_enum_values();
|
def->enum_keys_map = &ConfigOptionEnum<BeltRotationAxis>::get_enum_values();
|
||||||
def->enum_values = {"none", "x", "y", "z"};
|
def->enum_values = {"none", "x", "y", "z"};
|
||||||
def->enum_labels = {L("None"), L("X"), L("Y"), L("Z")};
|
def->enum_labels = {L("None"), L("X"), L("Y"), L("Z")};
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionEnum<BeltRotationAxis>(BeltRotationAxis::None));
|
def->set_default_value(new ConfigOptionEnum<BeltRotationAxis>(BeltRotationAxis::X));
|
||||||
|
|
||||||
def = this->add("belt_slice_rotation_angle", coFloat);
|
def = this->add("belt_slice_rotation_angle", coFloat);
|
||||||
def->label = L("Slicing rotation angle");
|
def->label = L("Belt tilt angle");
|
||||||
def->category = L("Printable space");
|
def->category = L("Printable space");
|
||||||
def->tooltip = L("Magnitude of the slicing rotation, in degrees. Positive values "
|
def->tooltip = L("Tilt angle of the belt surface, in degrees. Most belt printers use "
|
||||||
"rotate counter-clockwise looking down the positive axis.");
|
"45°. Positive values rotate counter-clockwise looking down the "
|
||||||
|
"positive tilt axis; the magnitude is also the physical belt tilt "
|
||||||
|
"used for bed rendering and support gravity.");
|
||||||
def->sidetext = L("°");
|
def->sidetext = L("°");
|
||||||
def->min = -180.;
|
def->min = -180.;
|
||||||
def->max = 180.;
|
def->max = 180.;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloat(0.));
|
def->set_default_value(new ConfigOptionFloat(45.));
|
||||||
|
|
||||||
def = this->add("belt_slice_rotation_global", coBool);
|
def = this->add("belt_slice_rotation_global", coBool);
|
||||||
def->label = L("Global");
|
def->label = L("Global");
|
||||||
def->category = L("Printable space");
|
def->category = L("Printable space");
|
||||||
def->tooltip = L("Treat the slicing rotation as part of the global forward transform "
|
def->tooltip = L("Treat the slicing rotation as part of the global forward transform "
|
||||||
"that BeltBackTransform inverts before the machine-frame remap. "
|
"that BeltBackTransform inverts before the machine-frame remap. "
|
||||||
"Required for rotation-mode belt printers; mirrors belt_shear_z_global. "
|
"Required for rotation-mode belt printers. "
|
||||||
"Defaults to on because virtually all rotation-mode printers need it.");
|
"Defaults to on because virtually all rotation-mode printers need it.");
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(true));
|
def->set_default_value(new ConfigOptionBool(true));
|
||||||
@@ -6553,12 +6450,6 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->set_default_value(new ConfigOptionEnum<BeltTransformOrder>(BeltTransformOrder::ShearThenScale));
|
def->set_default_value(new ConfigOptionEnum<BeltTransformOrder>(BeltTransformOrder::ShearThenScale));
|
||||||
};
|
};
|
||||||
|
|
||||||
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,
|
auto add_belt_remap = [this](const char *key, const char *label, const char *tooltip,
|
||||||
RemapAxis default_axis, ConfigOptionMode mode = comSimple) {
|
RemapAxis default_axis, ConfigOptionMode mode = comSimple) {
|
||||||
@@ -6599,7 +6490,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->tooltip = L("When enabled, the pre-slice axis remap accounts for each object's bed position. "
|
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 "
|
"Without this, the remap is applied locally around each object's center, so "
|
||||||
"objects at different positions don't get a position-dependent contribution. "
|
"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.");
|
"Mirrors the 'Global' option on the belt slicing rotation, but for the remap.");
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
@@ -6609,6 +6500,69 @@ void PrintConfigDef::init_fff_params()
|
|||||||
|
|
||||||
// Machine-frame G-code transforms: applied AFTER back-transform and gcode_remap,
|
// 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.
|
// 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_mode ("gcode_shear_x", "Function", BeltShearMode::None, comExpert);
|
||||||
add_belt_shear_angle("gcode_shear_x_angle", "Angle", 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_axis_enum ("gcode_shear_x_from", "From", "Source axis for X shear in the machine-frame stage.", BeltAxis::Z, comExpert);
|
||||||
@@ -6635,19 +6589,6 @@ void PrintConfigDef::init_fff_params()
|
|||||||
"applied to G-code coordinates. 'Scale, then shear' applies scale first and then "
|
"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.");
|
"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");
|
||||||
@@ -11801,16 +11742,22 @@ Polygons get_bed_excluded_area(const PrintConfig& cfg)
|
|||||||
{
|
{
|
||||||
const Pointfs exclude_area_points = cfg.bed_exclude_area.values;
|
const Pointfs exclude_area_points = cfg.bed_exclude_area.values;
|
||||||
|
|
||||||
// Belt printer: project exclusion zone points from belt surface to machine-frame XY.
|
// Belt printer: project exclusion zone points from the belt surface to machine-frame XY.
|
||||||
// On the belt surface, Z=0, so machine_Y = belt_Y * cos(angle).
|
// On the belt surface Z=0, so the in-plane axis foreshortens by cos(tilt). The tilt
|
||||||
|
// axis decides which bed axis foreshortens: tilt about X (belt along Y) scales Y,
|
||||||
|
// tilt about Y (belt along X) scales X. Derived from belt_slice_rotation.
|
||||||
const bool is_belt = cfg.belt_printer.value;
|
const bool is_belt = cfg.belt_printer.value;
|
||||||
const double belt_cos = is_belt ? std::cos(Geometry::deg2rad(cfg.belt_printer_angle.value)) : 1.0;
|
const auto tilt = BeltTransformPipeline::physical_tilt(
|
||||||
|
cfg.belt_slice_rotation.value, cfg.belt_slice_rotation_angle.value);
|
||||||
|
const double cos_x = is_belt ? std::cos(Geometry::deg2rad(tilt.tilt_x_deg)) : 1.0; // foreshortens Y
|
||||||
|
const double cos_y = is_belt ? std::cos(Geometry::deg2rad(tilt.tilt_y_deg)) : 1.0; // foreshortens X
|
||||||
|
|
||||||
Polygon exclude_poly;
|
Polygon exclude_poly;
|
||||||
for (int i = 0; i < exclude_area_points.size(); i++) {
|
for (int i = 0; i < exclude_area_points.size(); i++) {
|
||||||
auto pt = exclude_area_points[i];
|
auto pt = exclude_area_points[i];
|
||||||
double y = is_belt ? pt.y() * belt_cos : pt.y();
|
double x = is_belt ? pt.x() * cos_y : pt.x();
|
||||||
exclude_poly.points.emplace_back(scale_(pt.x()), scale_(y));
|
double y = is_belt ? pt.y() * cos_x : pt.y();
|
||||||
|
exclude_poly.points.emplace_back(scale_(x), scale_(y));
|
||||||
}
|
}
|
||||||
|
|
||||||
exclude_poly.make_counter_clockwise();
|
exclude_poly.make_counter_clockwise();
|
||||||
|
|||||||
@@ -1573,29 +1573,13 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||||||
((ConfigOptionFloat, build_plate_tilt_y))
|
((ConfigOptionFloat, build_plate_tilt_y))
|
||||||
// Belt printer settings (printer-level).
|
// Belt printer settings (printer-level).
|
||||||
((ConfigOptionBool, belt_printer))
|
((ConfigOptionBool, belt_printer))
|
||||||
((ConfigOptionFloat, belt_printer_angle))
|
|
||||||
((ConfigOptionBool, belt_printer_infinite_y))
|
((ConfigOptionBool, belt_printer_infinite_y))
|
||||||
((ConfigOptionEnum<BeltShearMode>, belt_shear_x))
|
// Mesh rotation applied before slicing — the sole mesh-side belt transform and
|
||||||
((ConfigOptionFloat, belt_shear_x_angle))
|
// the single source of truth for the physical belt tilt (its angle + axis drive
|
||||||
((ConfigOptionEnum<BeltAxis>, belt_shear_x_from))
|
// bed rendering, support gravity tilt, and the bed-exclusion projection).
|
||||||
((ConfigOptionBool, belt_shear_x_global))
|
// Isometric (no distortion); the g-code back-transform inverts it before the
|
||||||
((ConfigOptionEnum<BeltShearMode>, belt_shear_y))
|
// machine-frame remap. (Shear & scale are applied to the g-code, not the
|
||||||
((ConfigOptionFloat, belt_shear_y_angle))
|
// mesh — see gcode_shear_* / gcode_scale_* below.)
|
||||||
((ConfigOptionEnum<BeltAxis>, belt_shear_y_from))
|
|
||||||
((ConfigOptionBool, belt_shear_y_global))
|
|
||||||
((ConfigOptionEnum<BeltShearMode>, belt_shear_z))
|
|
||||||
((ConfigOptionFloat, belt_shear_z_angle))
|
|
||||||
((ConfigOptionEnum<BeltAxis>, belt_shear_z_from))
|
|
||||||
((ConfigOptionBool, belt_shear_z_global))
|
|
||||||
((ConfigOptionEnum<BeltScaleMode>, belt_scale_x))
|
|
||||||
((ConfigOptionFloat, belt_scale_x_angle))
|
|
||||||
((ConfigOptionEnum<BeltScaleMode>, belt_scale_y))
|
|
||||||
((ConfigOptionFloat, belt_scale_y_angle))
|
|
||||||
((ConfigOptionEnum<BeltScaleMode>, belt_scale_z))
|
|
||||||
((ConfigOptionFloat, belt_scale_z_angle))
|
|
||||||
// Global mesh rotation as an alternative to per-axis shear/scale (isometric
|
|
||||||
// slicing transform). Composes with shear in the pipeline math; UI gates
|
|
||||||
// them as mutually exclusive.
|
|
||||||
((ConfigOptionEnum<BeltRotationAxis>, belt_slice_rotation))
|
((ConfigOptionEnum<BeltRotationAxis>, belt_slice_rotation))
|
||||||
((ConfigOptionFloat, belt_slice_rotation_angle))
|
((ConfigOptionFloat, belt_slice_rotation_angle))
|
||||||
((ConfigOptionBool, belt_slice_rotation_global))
|
((ConfigOptionBool, belt_slice_rotation_global))
|
||||||
@@ -1621,11 +1605,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||||||
((ConfigOptionFloat, gcode_scale_y_angle))
|
((ConfigOptionFloat, gcode_scale_y_angle))
|
||||||
((ConfigOptionEnum<BeltScaleMode>, gcode_scale_z))
|
((ConfigOptionEnum<BeltScaleMode>, gcode_scale_z))
|
||||||
((ConfigOptionFloat, gcode_scale_z_angle))
|
((ConfigOptionFloat, gcode_scale_z_angle))
|
||||||
((ConfigOptionEnum<BeltTransformOrder>, belt_mesh_transform_order))
|
|
||||||
((ConfigOptionEnum<BeltTransformOrder>, belt_gcode_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))
|
||||||
|
|||||||
@@ -341,9 +341,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||||||
// pushes those layers into the parallel_for path below, which handles multi-volume
|
// pushes those layers into the parallel_for path below, which handles multi-volume
|
||||||
// clipping per layer without relying on the bbox Z range.
|
// clipping per layer without relying on the bbox Z range.
|
||||||
const bool bbox_z_in_layer_frame = !(print_config.belt_printer.value &&
|
const bool bbox_z_in_layer_frame = !(print_config.belt_printer.value &&
|
||||||
(BeltTransformPipeline::has_shear(print_config)
|
(BeltTransformPipeline::has_rotation(print_config)
|
||||||
|| BeltTransformPipeline::has_scale(print_config)
|
|
||||||
|| BeltTransformPipeline::has_rotation(print_config)
|
|
||||||
|| BeltTransformPipeline::has_preslice_remap(print_config)));
|
|| BeltTransformPipeline::has_preslice_remap(print_config)));
|
||||||
// Belt-transform addendum: with bbox-Z untrusted, the simple path's
|
// Belt-transform addendum: with bbox-Z untrusted, the simple path's
|
||||||
// "first model_part wins" logic drops subsequent volumes' slices unless
|
// "first model_part wins" logic drops subsequent volumes' slices unless
|
||||||
@@ -880,12 +878,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()
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] slice request tid=" << std::this_thread::get_id() << " obj=" << this;
|
BOOST_LOG_TRIVIAL(trace) << "[BELTRACE] slice request tid=" << std::this_thread::get_id() << " obj=" << this;
|
||||||
if (! this->set_started(posSlice)) {
|
if (! this->set_started(posSlice)) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] slice SKIP tid=" << std::this_thread::get_id() << " obj=" << this << " (already started/done)";
|
BOOST_LOG_TRIVIAL(trace) << "[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;
|
BOOST_LOG_TRIVIAL(trace) << "[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;
|
||||||
@@ -968,16 +966,16 @@ void PrintObject::slice()
|
|||||||
// regardless of global mode, only the output Z coordinates change.
|
// regardless of global mode, only the output Z coordinates change.
|
||||||
{
|
{
|
||||||
const auto &pcfg = this->print()->config();
|
const auto &pcfg = this->print()->config();
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Belt global check: belt_printer=" << pcfg.belt_printer.value
|
BOOST_LOG_TRIVIAL(trace) << "Belt global check: belt_printer=" << pcfg.belt_printer.value
|
||||||
<< " belt_shear_z=" << int(pcfg.belt_shear_z.value)
|
<< " belt_slice_rotation=" << int(pcfg.belt_slice_rotation.value)
|
||||||
<< " belt_shear_z_global=" << pcfg.belt_shear_z_global.value
|
<< " belt_slice_rotation_global=" << pcfg.belt_slice_rotation_global.value
|
||||||
<< " belt_preslice_global=" << pcfg.belt_preslice_global.value
|
<< " belt_preslice_global=" << pcfg.belt_preslice_global.value
|
||||||
<< " object=" << this->model_object()->name;
|
<< " object=" << this->model_object()->name;
|
||||||
if (pcfg.belt_printer.value) {
|
if (pcfg.belt_printer.value) {
|
||||||
|
|
||||||
Point inst_shift = this->instances().empty() ? Point(0, 0)
|
Point inst_shift = this->instances().empty() ? Point(0, 0)
|
||||||
: this->instances().front().shift - this->center_offset();
|
: this->instances().front().shift - this->center_offset();
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Belt global: object " << this->model_object()->name
|
BOOST_LOG_TRIVIAL(trace) << "Belt global: object " << this->model_object()->name
|
||||||
<< " instances=" << this->instances().size()
|
<< " instances=" << this->instances().size()
|
||||||
<< " shift=(" << unscale<double>(inst_shift.x()) << ", " << unscale<double>(inst_shift.y()) << ")";
|
<< " shift=(" << unscale<double>(inst_shift.x()) << ", " << unscale<double>(inst_shift.y()) << ")";
|
||||||
|
|
||||||
@@ -987,20 +985,14 @@ void PrintObject::slice()
|
|||||||
// so the slicer can slice with slicer_z >= 0. BeltBackTransform inverts
|
// so the slicer can slice with slicer_z >= 0. BeltBackTransform inverts
|
||||||
// build_forward_transform() which DOES NOT include this per-object
|
// build_forward_transform() which DOES NOT include this per-object
|
||||||
// Z-shift (it's not known until vertex scan time). Result: G-code
|
// Z-shift (it's not known until vertex scan time). Result: G-code
|
||||||
// coords emerge offset by the un-undone Z-shift — for shear that's a
|
// coords emerge offset by the un-undone Z-shift — the inverse rotation
|
||||||
// pure machine_z lift; for rotation it leaks into both machine_y and
|
// couples slicer_z back into both machine_y and machine_z. Compensating
|
||||||
// machine_z because the inverse rotation couples slicer_z back into
|
// layer.print_z by belt_z_shift here makes the back-transform produce
|
||||||
// both axes. Compensating layer.print_z by shear_min_z here makes the
|
// correct machine-frame coordinates whether or not a global mode is active.
|
||||||
// back-transform produce correct machine-frame coordinates whether or
|
|
||||||
// not any global mode is active.
|
|
||||||
//
|
|
||||||
// (The original global-mode-only application of shear_min_z was sized
|
|
||||||
// for cube-vs-inverted-cone-tip differentiation; the same compensation
|
|
||||||
// is what fixes assemblies and rotation-mode parts where z_shift > 0.)
|
|
||||||
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 belt_z_shift = m_belt_min_z - belt_surface_z;
|
||||||
double global_z_offset = shear_min_z;
|
double global_z_offset = belt_z_shift;
|
||||||
|
|
||||||
// Centering correction: trafo_centered pretranslates by
|
// Centering correction: trafo_centered pretranslates by
|
||||||
// -m_center_offset.{x,y}. Under the belt forward transform, the
|
// -m_center_offset.{x,y}. Under the belt forward transform, the
|
||||||
@@ -1019,7 +1011,7 @@ void PrintObject::slice()
|
|||||||
0.);
|
0.);
|
||||||
double centering_z_corr = (T_fwd.linear() * c_off).z();
|
double centering_z_corr = (T_fwd.linear() * c_off).z();
|
||||||
global_z_offset += centering_z_corr;
|
global_z_offset += centering_z_corr;
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] centering correction"
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] centering correction"
|
||||||
<< " obj=" << this->model_object()->name
|
<< " obj=" << this->model_object()->name
|
||||||
<< " m_center_offset_mm=(" << c_off.x() << "," << c_off.y() << ")"
|
<< " m_center_offset_mm=(" << c_off.x() << "," << c_off.y() << ")"
|
||||||
<< " centering_z_corr=" << centering_z_corr
|
<< " centering_z_corr=" << centering_z_corr
|
||||||
@@ -1031,7 +1023,7 @@ void PrintObject::slice()
|
|||||||
// print_z adjustment.
|
// print_z adjustment.
|
||||||
{
|
{
|
||||||
BoundingBoxf3 raw_bb = this->model_object()->raw_bounding_box();
|
BoundingBoxf3 raw_bb = this->model_object()->raw_bounding_box();
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] slice() per-object summary"
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] slice() per-object summary"
|
||||||
<< " obj=" << this->model_object()->name
|
<< " obj=" << this->model_object()->name
|
||||||
<< " n_volumes=" << this->model_object()->volumes.size()
|
<< " n_volumes=" << this->model_object()->volumes.size()
|
||||||
<< " raw_bbox.min=(" << raw_bb.min.x() << "," << raw_bb.min.y() << "," << raw_bb.min.z() << ")"
|
<< " raw_bbox.min=(" << raw_bb.min.x() << "," << raw_bb.min.y() << "," << raw_bb.min.z() << ")"
|
||||||
@@ -1041,13 +1033,13 @@ void PrintObject::slice()
|
|||||||
<< " inst_shift=(" << unscale<double>(inst_shift.x()) << "," << unscale<double>(inst_shift.y()) << ")"
|
<< " inst_shift=(" << unscale<double>(inst_shift.x()) << "," << unscale<double>(inst_shift.y()) << ")"
|
||||||
<< " m_belt_min_z=" << m_belt_min_z
|
<< " m_belt_min_z=" << m_belt_min_z
|
||||||
<< " belt_surface_z=" << belt_surface_z
|
<< " belt_surface_z=" << belt_surface_z
|
||||||
<< " shear_min_z=" << shear_min_z;
|
<< " belt_z_shift=" << belt_z_shift;
|
||||||
// Per-volume bbox + get_matrix translation so order/composition is visible.
|
// Per-volume bbox + get_matrix translation so order/composition is visible.
|
||||||
int vi = 0;
|
int vi = 0;
|
||||||
for (const ModelVolume *mv : this->model_object()->volumes) {
|
for (const ModelVolume *mv : this->model_object()->volumes) {
|
||||||
if (!mv->is_model_part()) { ++vi; continue; }
|
if (!mv->is_model_part()) { ++vi; continue; }
|
||||||
BoundingBoxf3 vol_bb = mv->mesh().transformed_bounding_box(mv->get_matrix());
|
BoundingBoxf3 vol_bb = mv->mesh().transformed_bounding_box(mv->get_matrix());
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] vol[" << vi
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] vol[" << vi
|
||||||
<< "] id=" << mv->id().id << " name='" << mv->name << "'"
|
<< "] id=" << mv->id().id << " name='" << mv->name << "'"
|
||||||
<< " get_matrix.translation=(" << mv->get_matrix().translation().x() << "," << mv->get_matrix().translation().y() << "," << mv->get_matrix().translation().z() << ")"
|
<< " get_matrix.translation=(" << mv->get_matrix().translation().x() << "," << mv->get_matrix().translation().y() << "," << mv->get_matrix().translation().z() << ")"
|
||||||
<< " object_bbox.min=(" << vol_bb.min.x() << "," << vol_bb.min.y() << "," << vol_bb.min.z() << ")"
|
<< " object_bbox.min=(" << vol_bb.min.x() << "," << vol_bb.min.y() << "," << vol_bb.min.z() << ")"
|
||||||
@@ -1063,46 +1055,15 @@ 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();
|
global_z_offset += c.z();
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] write m_belt_global_xy_correction tid=" << std::this_thread::get_id()
|
BOOST_LOG_TRIVIAL(trace) << "[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()
|
<< " obj=" << this << " old=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y()
|
||||||
<< ") new=(" << c.x() << "," << c.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(trace) << "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 << ")";
|
<< " belt_z_shift=" << belt_z_shift << " (m_belt_min_z=" << m_belt_min_z << ")";
|
||||||
} else {
|
} 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) {
|
|
||||||
// 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;
|
|
||||||
global_z_offset += c.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());
|
|
||||||
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 << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slicing rotation in global mode: bed-position-dependent Z offset.
|
// Slicing rotation in global mode: bed-position-dependent Z offset.
|
||||||
// For R(α, X): c.z = sin(α)*d.y so objects at different bed-Y
|
// For R(α, X): c.z = sin(α)*d.y so objects at different bed-Y
|
||||||
// values print at different machine Z values along the inclined belt.
|
// values print at different machine Z values along the inclined belt.
|
||||||
@@ -1129,16 +1090,16 @@ void PrintObject::slice()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Belt global: z_offset=" << global_z_offset
|
BOOST_LOG_TRIVIAL(trace) << "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()
|
BOOST_LOG_TRIVIAL(trace) << "[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;
|
<< " 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;
|
||||||
// [BELT-DEBUG] Final breakdown of all contributions to layer.print_z
|
// [BELT-DEBUG] Final breakdown of all contributions to layer.print_z
|
||||||
// and where the first / last layer end up post-adjustment.
|
// and where the first / last layer end up post-adjustment.
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] global_z_offset breakdown"
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] global_z_offset breakdown"
|
||||||
<< " obj=" << this->model_object()->name
|
<< " obj=" << this->model_object()->name
|
||||||
<< " shear_min_z=" << shear_min_z
|
<< " belt_z_shift=" << belt_z_shift
|
||||||
<< " total_global_z_offset=" << global_z_offset
|
<< " total_global_z_offset=" << global_z_offset
|
||||||
<< " xy_correction=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y() << ")"
|
<< " xy_correction=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y() << ")"
|
||||||
<< " belt_floor_z_shift_before=" << (m_slicing_params.belt_floor_z_shift)
|
<< " belt_floor_z_shift_before=" << (m_slicing_params.belt_floor_z_shift)
|
||||||
@@ -1152,13 +1113,13 @@ void PrintObject::slice()
|
|||||||
m_slicing_params.belt_floor_z_shift += global_z_offset;
|
m_slicing_params.belt_floor_z_shift += global_z_offset;
|
||||||
}
|
}
|
||||||
if (!m_layers.empty()) {
|
if (!m_layers.empty()) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] post-adjustment"
|
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] post-adjustment"
|
||||||
<< " first_layer.print_z=" << m_layers.front()->print_z
|
<< " first_layer.print_z=" << m_layers.front()->print_z
|
||||||
<< " last_layer.print_z=" << m_layers.back()->print_z
|
<< " last_layer.print_z=" << m_layers.back()->print_z
|
||||||
<< " belt_floor_z_shift_after=" << m_slicing_params.belt_floor_z_shift;
|
<< " belt_floor_z_shift_after=" << m_slicing_params.belt_floor_z_shift;
|
||||||
}
|
}
|
||||||
if (!m_layers.empty()) {
|
if (!m_layers.empty()) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "Belt global: first_layer_z=" << m_layers.front()->print_z
|
BOOST_LOG_TRIVIAL(trace) << "Belt global: first_layer_z=" << m_layers.front()->print_z
|
||||||
<< " last_layer_z=" << m_layers.back()->print_z
|
<< " last_layer_z=" << m_layers.back()->print_z
|
||||||
<< " num_layers=" << m_layers.size()
|
<< " num_layers=" << m_layers.size()
|
||||||
<< " center_offset=(" << unscale<double>(m_center_offset.x())
|
<< " center_offset=(" << unscale<double>(m_center_offset.x())
|
||||||
@@ -1175,7 +1136,7 @@ void PrintObject::slice()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BBS
|
// BBS
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[BELTRACE] slice EXIT tid=" << std::this_thread::get_id() << " obj=" << this
|
BOOST_LOG_TRIVIAL(trace) << "[BELTRACE] slice EXIT tid=" << std::this_thread::get_id() << " obj=" << this
|
||||||
<< " layers=" << m_layers.size() << " belt_min_z=" << m_belt_min_z
|
<< " layers=" << m_layers.size() << " belt_min_z=" << m_belt_min_z
|
||||||
<< " belt_global_z_offset=" << m_belt_global_z_offset
|
<< " belt_global_z_offset=" << m_belt_global_z_offset
|
||||||
<< " belt_xy=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y() << ")";
|
<< " belt_xy=(" << m_belt_global_xy_correction.x() << "," << m_belt_global_xy_correction.y() << ")";
|
||||||
|
|||||||
+12
-13
@@ -700,11 +700,11 @@ void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& proj
|
|||||||
shader->start_using();
|
shader->start_using();
|
||||||
shader->set_uniform("emission_factor", 0.0f);
|
shader->set_uniform("emission_factor", 0.0f);
|
||||||
Transform3d model_matrix = Geometry::assemble_transform(m_model_offset);
|
Transform3d model_matrix = Geometry::assemble_transform(m_model_offset);
|
||||||
// Belt printer: rotate the bed model about X so the belt tilt is visible.
|
// Belt printer: rotate the bed model about the tilt axis so the belt tilt
|
||||||
// Negative angle: belt surface tilts downward away from the nozzle.
|
// is visible. Negative angle: belt surface tilts downward away from the nozzle.
|
||||||
if (m_is_belt_printer && m_belt_angle > 0.f) {
|
if (m_is_belt_printer && m_belt_angle > 0.f) {
|
||||||
double angle_rad = Geometry::deg2rad(static_cast<double>(m_belt_angle));
|
double angle_rad = Geometry::deg2rad(static_cast<double>(m_belt_angle));
|
||||||
model_matrix = Eigen::AngleAxisd(-angle_rad, Vec3d::UnitX()) * model_matrix;
|
model_matrix = Eigen::AngleAxisd(-angle_rad, belt_tilt_unit_axis()) * model_matrix;
|
||||||
}
|
}
|
||||||
shader->set_uniform("volume_world_matrix", model_matrix);
|
shader->set_uniform("volume_world_matrix", model_matrix);
|
||||||
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
|
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
|
||||||
@@ -747,12 +747,10 @@ void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, co
|
|||||||
void Bed3D::render_gravity_arrow(const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
void Bed3D::render_gravity_arrow(const Transform3d& view_matrix, const Transform3d& projection_matrix)
|
||||||
{
|
{
|
||||||
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
|
// build_plate_tilt_{x,y} are kept in sync with the belt tilt (see TabPrinter), so
|
||||||
|
// reading them here covers both belt and non-belt tilted printers.
|
||||||
double tilt_x_deg = cfg.opt_float("build_plate_tilt_x");
|
double tilt_x_deg = cfg.opt_float("build_plate_tilt_x");
|
||||||
double tilt_y_deg = cfg.opt_float("build_plate_tilt_y");
|
double tilt_y_deg = cfg.opt_float("build_plate_tilt_y");
|
||||||
// Belt printer: auto-derive gravity direction from belt angle if belt mode is active.
|
|
||||||
if (m_is_belt_printer && m_belt_angle > 0.f) {
|
|
||||||
tilt_x_deg = m_belt_angle;
|
|
||||||
}
|
|
||||||
if (tilt_x_deg == 0. && tilt_y_deg == 0.) {
|
if (tilt_x_deg == 0. && tilt_y_deg == 0.) {
|
||||||
m_gravity_arrow.reset();
|
m_gravity_arrow.reset();
|
||||||
return;
|
return;
|
||||||
@@ -819,11 +817,12 @@ void Bed3D::render_slicing_arrow(const Transform3d& view_matrix, const Transform
|
|||||||
m_slicing_arrow.init_from(stilized_arrow(16, tip_radius, tip_length, stem_radius, stem_length));
|
m_slicing_arrow.init_from(stilized_arrow(16, tip_radius, tip_length, stem_radius, stem_length));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The slicing direction: layers stack along the gantry normal.
|
// The slicing direction: layers stack along the gantry normal, i.e. the image of
|
||||||
// With mesh rotation R(-alpha, X), the slicing Z-axis in the original frame
|
// +Z under the mesh rotation about the tilt axis. Use the same AngleAxis as the
|
||||||
// points in direction R(+alpha, X) * (0, 0, 1) = (0, -sin(alpha), cos(alpha)).
|
// slicing pipeline so the arrow matches whichever tilt axis is configured.
|
||||||
double angle_rad = Geometry::deg2rad(static_cast<double>(m_belt_angle));
|
double angle_rad = Geometry::deg2rad(static_cast<double>(m_belt_angle));
|
||||||
Vec3d slice_dir = Vec3d(0., -std::sin(angle_rad), std::cos(angle_rad)).normalized();
|
Vec3d slice_dir = (Eigen::AngleAxisd(angle_rad, belt_tilt_unit_axis()).toRotationMatrix()
|
||||||
|
* Vec3d::UnitZ()).normalized();
|
||||||
|
|
||||||
// Compute rotation to align +Z (arrow default) with slice_dir.
|
// Compute rotation to align +Z (arrow default) with slice_dir.
|
||||||
Vec3d from = Vec3d::UnitZ();
|
Vec3d from = Vec3d::UnitZ();
|
||||||
@@ -895,12 +894,12 @@ void Bed3D::render_slicing_plane(const Transform3d& view_matrix, const Transform
|
|||||||
shader->start_using();
|
shader->start_using();
|
||||||
|
|
||||||
// Show a tilted plane representing the slicing direction.
|
// Show a tilted plane representing the slicing direction.
|
||||||
// The slicing plane is rotated by belt_angle about X from horizontal.
|
// The slicing plane is rotated by belt_angle about the tilt axis from horizontal.
|
||||||
// Raise it slightly so it's visible above the bed surface.
|
// Raise it slightly so it's visible above the bed surface.
|
||||||
double angle_rad = Geometry::deg2rad(static_cast<double>(m_belt_angle));
|
double angle_rad = Geometry::deg2rad(static_cast<double>(m_belt_angle));
|
||||||
Transform3d model_matrix = Transform3d::Identity();
|
Transform3d model_matrix = Transform3d::Identity();
|
||||||
model_matrix.translate(Vec3d(0., 0., 30.));
|
model_matrix.translate(Vec3d(0., 0., 30.));
|
||||||
model_matrix.rotate(Eigen::AngleAxisd(angle_rad, Vec3d::UnitX()));
|
model_matrix.rotate(Eigen::AngleAxisd(angle_rad, belt_tilt_unit_axis()));
|
||||||
|
|
||||||
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
|
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
|
||||||
shader->set_uniform("projection_matrix", projection_matrix);
|
shader->set_uniform("projection_matrix", projection_matrix);
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ private:
|
|||||||
// Belt printer state for rendering.
|
// Belt printer state for rendering.
|
||||||
bool m_is_belt_printer = false;
|
bool m_is_belt_printer = false;
|
||||||
float m_belt_angle = 0.f;
|
float m_belt_angle = 0.f;
|
||||||
|
// Tilt axis: 0 = X (belt travels along Y, the common case), 1 = Y.
|
||||||
|
int m_belt_tilt_axis = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Bed3D() = default;
|
Bed3D() = default;
|
||||||
@@ -146,10 +148,14 @@ public:
|
|||||||
const BuildVolume& build_volume() const { return m_build_volume; }
|
const BuildVolume& build_volume() const { return m_build_volume; }
|
||||||
BuildVolume& build_volume() { return m_build_volume; }
|
BuildVolume& build_volume() { return m_build_volume; }
|
||||||
|
|
||||||
// Belt printer bed settings.
|
// Belt printer bed settings. tilt_axis: 0 = X (belt along Y), 1 = Y.
|
||||||
void set_belt_printer(bool enabled, float angle_deg) { m_is_belt_printer = enabled; m_belt_angle = angle_deg; }
|
void set_belt_printer(bool enabled, float angle_deg, int tilt_axis = 0) {
|
||||||
|
m_is_belt_printer = enabled; m_belt_angle = angle_deg; m_belt_tilt_axis = tilt_axis;
|
||||||
|
}
|
||||||
bool is_belt_printer() const { return m_is_belt_printer; }
|
bool is_belt_printer() const { return m_is_belt_printer; }
|
||||||
float belt_angle() const { return m_belt_angle; }
|
float belt_angle() const { return m_belt_angle; }
|
||||||
|
// Unit vector of the tilt axis in bed space.
|
||||||
|
Vec3d belt_tilt_unit_axis() const { return m_belt_tilt_axis == 1 ? Vec3d::UnitY() : Vec3d::UnitX(); }
|
||||||
|
|
||||||
// Was the model provided, or was it generated procedurally?
|
// Was the model provided, or was it generated procedurally?
|
||||||
Type get_type() const { return m_type; }
|
Type get_type() const { return m_type; }
|
||||||
|
|||||||
+23
-70
@@ -11312,80 +11312,33 @@ void Plater::priv::set_bed_shape(const Pointfs &shape,
|
|||||||
const auto *belt_opt = config->option<ConfigOptionBool>("belt_printer");
|
const auto *belt_opt = config->option<ConfigOptionBool>("belt_printer");
|
||||||
bool is_belt = belt_opt && belt_opt->value;
|
bool is_belt = belt_opt && belt_opt->value;
|
||||||
if (is_belt) {
|
if (is_belt) {
|
||||||
double belt_angle = config->opt_float("belt_printer_angle");
|
// The slicing rotation is the single source of truth for the belt tilt:
|
||||||
|
// its magnitude is the physical tilt angle and its axis is the tilt axis.
|
||||||
|
auto rot_axis = config->option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation")->value;
|
||||||
|
double rot_angle = config->opt_float("belt_slice_rotation_angle");
|
||||||
|
double belt_angle = std::abs(rot_angle); // physical tilt magnitude
|
||||||
|
int tilt_axis = (rot_axis == BeltRotationAxis::Y) ? 1 : 0;
|
||||||
bool infinite_y = config->opt_bool("belt_printer_infinite_y");
|
bool infinite_y = config->opt_bool("belt_printer_infinite_y");
|
||||||
bed.build_volume().set_belt_printer(true, belt_angle, infinite_y);
|
bed.build_volume().set_belt_printer(true, belt_angle, infinite_y);
|
||||||
bed.set_belt_printer(true, static_cast<float>(belt_angle));
|
bed.set_belt_printer(true, static_cast<float>(belt_angle), tilt_axis);
|
||||||
if (preview)
|
if (preview)
|
||||||
preview->get_canvas3d()->get_gcode_viewer().set_belt_printer(true, static_cast<float>(belt_angle));
|
preview->get_canvas3d()->get_gcode_viewer().set_belt_printer(true, static_cast<float>(belt_angle));
|
||||||
|
|
||||||
// Compute the inverse of the full belt shear+scale transform for the G-code viewer.
|
// Compute the inverse of the mesh-side belt transform for the G-code
|
||||||
auto compute_shear_factor = [](BeltShearMode mode, double angle_deg) -> double {
|
// viewer. The sole mesh transform is the slicing rotation; build its
|
||||||
double angle_rad = Geometry::deg2rad(angle_deg);
|
// matrix from config and hand the viewer its inverse (rotation is
|
||||||
double sin_a = std::sin(angle_rad);
|
// orthogonal, so the inverse is the transpose).
|
||||||
double cos_a = std::cos(angle_rad);
|
Transform3d forward = Transform3d::Identity();
|
||||||
switch (mode) {
|
if (rot_axis != BeltRotationAxis::None && std::abs(rot_angle) > EPSILON) {
|
||||||
case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.;
|
Vec3d unit_axis = Vec3d::UnitX();
|
||||||
case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.;
|
switch (rot_axis) {
|
||||||
case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.;
|
case BeltRotationAxis::X: unit_axis = Vec3d::UnitX(); break;
|
||||||
case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.;
|
case BeltRotationAxis::Y: unit_axis = Vec3d::UnitY(); break;
|
||||||
default: return 0.;
|
case BeltRotationAxis::Z: unit_axis = Vec3d::UnitZ(); break;
|
||||||
|
default: break;
|
||||||
}
|
}
|
||||||
};
|
forward.linear() = Eigen::AngleAxisd(Geometry::deg2rad(rot_angle), unit_axis).toRotationMatrix();
|
||||||
auto compute_scale_factor = [](BeltScaleMode mode, double angle_deg) -> double {
|
|
||||||
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.;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Read shear configs.
|
|
||||||
auto get_shear_mode = [this](const char *key) -> BeltShearMode {
|
|
||||||
auto opt = config->option<ConfigOptionEnum<BeltShearMode>>(key);
|
|
||||||
return opt ? opt->value : BeltShearMode::None;
|
|
||||||
};
|
|
||||||
auto get_axis = [this](const char *key) -> BeltAxis {
|
|
||||||
auto opt = config->option<ConfigOptionEnum<BeltAxis>>(key);
|
|
||||||
return opt ? opt->value : BeltAxis::X;
|
|
||||||
};
|
|
||||||
auto get_scale_mode = [this](const char *key) -> BeltScaleMode {
|
|
||||||
auto opt = config->option<ConfigOptionEnum<BeltScaleMode>>(key);
|
|
||||||
return opt ? opt->value : BeltScaleMode::None;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AxisShear { BeltShearMode mode; double angle; int from; };
|
|
||||||
AxisShear axes[3] = {
|
|
||||||
{ get_shear_mode("belt_shear_x"), config->opt_float("belt_shear_x_angle"), int(get_axis("belt_shear_x_from")) },
|
|
||||||
{ get_shear_mode("belt_shear_y"), config->opt_float("belt_shear_y_angle"), int(get_axis("belt_shear_y_from")) },
|
|
||||||
{ get_shear_mode("belt_shear_z"), config->opt_float("belt_shear_z_angle"), int(get_axis("belt_shear_z_from")) },
|
|
||||||
};
|
|
||||||
|
|
||||||
Transform3d belt_shear = Transform3d::Identity();
|
|
||||||
for (int row = 0; row < 3; ++row) {
|
|
||||||
if (axes[row].mode != BeltShearMode::None) {
|
|
||||||
double factor = compute_shear_factor(axes[row].mode, axes[row].angle);
|
|
||||||
if (std::abs(factor) > EPSILON)
|
|
||||||
belt_shear.matrix()(row, axes[row].from) += factor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double sx = compute_scale_factor(get_scale_mode("belt_scale_x"), config->opt_float("belt_scale_x_angle"));
|
|
||||||
double sy = compute_scale_factor(get_scale_mode("belt_scale_y"), config->opt_float("belt_scale_y_angle"));
|
|
||||||
double sz = compute_scale_factor(get_scale_mode("belt_scale_z"), config->opt_float("belt_scale_z_angle"));
|
|
||||||
Transform3d belt_scale = Transform3d::Identity();
|
|
||||||
belt_scale.matrix()(0, 0) = sx;
|
|
||||||
belt_scale.matrix()(1, 1) = sy;
|
|
||||||
belt_scale.matrix()(2, 2) = sz;
|
|
||||||
|
|
||||||
// Forward transform: scale * shear. Inverse for the viewer.
|
|
||||||
Transform3d forward = belt_scale * belt_shear;
|
|
||||||
Transform3d inverse = forward.inverse();
|
Transform3d inverse = forward.inverse();
|
||||||
if (preview)
|
if (preview)
|
||||||
preview->get_canvas3d()->get_gcode_viewer().set_belt_inverse_transform(inverse);
|
preview->get_canvas3d()->get_gcode_viewer().set_belt_inverse_transform(inverse);
|
||||||
@@ -13730,10 +13683,10 @@ void Plater::load_gcode(const wxString& filename)
|
|||||||
|
|
||||||
current_print.set_gcode_file_ready();
|
current_print.set_gcode_file_ready();
|
||||||
|
|
||||||
// Belt printer: detect belt_printer_angle from loaded G-code header and enable
|
// Belt printer: detect the belt tilt from the loaded G-code header and enable
|
||||||
// belt view mode on the GCodeViewer so the "Show designed view" toggle appears.
|
// belt view mode on the GCodeViewer so the "Show designed view" toggle appears.
|
||||||
if (current_result->belt_printer_angle > 0.f) {
|
if (current_result->belt_tilt_angle > 0.f) {
|
||||||
float angle = current_result->belt_printer_angle;
|
float angle = current_result->belt_tilt_angle;
|
||||||
p->preview->get_canvas3d()->get_gcode_viewer().set_belt_printer(true, angle);
|
p->preview->get_canvas3d()->get_gcode_viewer().set_belt_printer(true, angle);
|
||||||
} else {
|
} else {
|
||||||
p->preview->get_canvas3d()->get_gcode_viewer().set_belt_printer(false, 0.f);
|
p->preview->get_canvas3d()->get_gcode_viewer().set_belt_printer(false, 0.f);
|
||||||
|
|||||||
+40
-146
@@ -4,6 +4,7 @@
|
|||||||
#include "PresetHints.hpp"
|
#include "PresetHints.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
#include "libslic3r/BeltTransform.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||||
@@ -4464,68 +4465,22 @@ void TabPrinter::build_fff()
|
|||||||
// checkbox is hidden when belt_printer is off (see TabPrinter::toggle_options).
|
// checkbox is hidden when belt_printer is off (see TabPrinter::toggle_options).
|
||||||
auto belt_og = page->new_optgroup(L("Belt printer"), L"param_advanced");
|
auto belt_og = page->new_optgroup(L("Belt printer"), L"param_advanced");
|
||||||
belt_og->append_single_option_line("belt_printer");
|
belt_og->append_single_option_line("belt_printer");
|
||||||
belt_og->append_single_option_line("belt_printer_angle");
|
|
||||||
belt_og->append_single_option_line("belt_printer_infinite_y");
|
belt_og->append_single_option_line("belt_printer_infinite_y");
|
||||||
// Mesh rotate (default belt-side transform): isometric, no distortion.
|
// Belt tilt: the sole mesh-side transform and the single source of truth for
|
||||||
// Shown above the per-axis shear/scale rows because most users should
|
// the physical tilt (drives bed rendering and support gravity tilt too).
|
||||||
// pick rotation; shear/scale is the expert escape hatch.
|
// Isometric rotation, no distortion; the back-transform inverts it before the
|
||||||
|
// machine-frame remap.
|
||||||
{
|
{
|
||||||
Line line = { L("Mesh rotate"),
|
Line line = { L("Belt tilt"),
|
||||||
L("Global mesh rotation applied before slicing. Isometric "
|
L("Belt tilt axis and angle, applied as a mesh rotation before "
|
||||||
"(no distortion); the back-transform inverts it before the "
|
"slicing. Also drives bed rendering and support gravity tilt. "
|
||||||
"machine-frame remap. Default belt transform. Mutually "
|
"Isometric (no distortion); the back-transform inverts it before "
|
||||||
"exclusive with per-axis shear/scale in the UI.") };
|
"the machine-frame remap.") };
|
||||||
line.append_option(belt_og->get_option("belt_slice_rotation"));
|
line.append_option(belt_og->get_option("belt_slice_rotation"));
|
||||||
line.append_option(belt_og->get_option("belt_slice_rotation_angle"));
|
line.append_option(belt_og->get_option("belt_slice_rotation_angle"));
|
||||||
line.append_option(belt_og->get_option("belt_slice_rotation_global"));
|
line.append_option(belt_og->get_option("belt_slice_rotation_global"));
|
||||||
belt_og->append_line(line);
|
belt_og->append_line(line);
|
||||||
}
|
}
|
||||||
// Per-axis shear/scale: expert escape hatch for non-rigid belt transforms
|
|
||||||
// (BlackBelt-style 1/sin scaling, asymmetric belt geometries, etc.).
|
|
||||||
// Group mode + angle + source on one row per axis.
|
|
||||||
{
|
|
||||||
Line line = { L("Mesh shear X"), L("Shear applied to the X axis before slicing") };
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_x"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_x_angle"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_x_from"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_x_global"));
|
|
||||||
belt_og->append_line(line);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Line line = { L("Mesh shear Y"), L("Shear applied to the Y axis before slicing") };
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_y"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_y_angle"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_y_from"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_y_global"));
|
|
||||||
belt_og->append_line(line);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Line line = { L("Mesh shear Z"), L("Shear applied to the Z axis before slicing") };
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_z"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_z_angle"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_z_from"));
|
|
||||||
line.append_option(belt_og->get_option("belt_shear_z_global"));
|
|
||||||
belt_og->append_line(line);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Line line = { L("Mesh scale X"), L("Scale applied to the X axis before slicing") };
|
|
||||||
line.append_option(belt_og->get_option("belt_scale_x"));
|
|
||||||
line.append_option(belt_og->get_option("belt_scale_x_angle"));
|
|
||||||
belt_og->append_line(line);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Line line = { L("Mesh scale Y"), L("Scale applied to the Y axis before slicing") };
|
|
||||||
line.append_option(belt_og->get_option("belt_scale_y"));
|
|
||||||
line.append_option(belt_og->get_option("belt_scale_y_angle"));
|
|
||||||
belt_og->append_line(line);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Line line = { L("Mesh scale Z"), L("Scale applied to the Z axis before slicing") };
|
|
||||||
line.append_option(belt_og->get_option("belt_scale_z"));
|
|
||||||
line.append_option(belt_og->get_option("belt_scale_z_angle"));
|
|
||||||
belt_og->append_line(line);
|
|
||||||
}
|
|
||||||
belt_og->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 "
|
||||||
@@ -4537,13 +4492,6 @@ void TabPrinter::build_fff()
|
|||||||
line.append_option(belt_og->get_option("preslice_remap_global"));
|
line.append_option(belt_og->get_option("preslice_remap_global"));
|
||||||
belt_og->append_line(line);
|
belt_og->append_line(line);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
Line line = { L("G-code axis remap (post-slice)"), L("Remap slicing-frame axes to machine axes in G-code output. Applied AFTER slicing, during G-code generation.") };
|
|
||||||
line.append_option(belt_og->get_option("gcode_remap_x"));
|
|
||||||
line.append_option(belt_og->get_option("gcode_remap_y"));
|
|
||||||
line.append_option(belt_og->get_option("gcode_remap_z"));
|
|
||||||
belt_og->append_line(line);
|
|
||||||
}
|
|
||||||
belt_og->append_single_option_line("belt_preslice_global");
|
belt_og->append_single_option_line("belt_preslice_global");
|
||||||
belt_og->append_single_option_line("gcode_back_transform");
|
belt_og->append_single_option_line("gcode_back_transform");
|
||||||
{
|
{
|
||||||
@@ -4588,12 +4536,10 @@ void TabPrinter::build_fff()
|
|||||||
{
|
{
|
||||||
auto mf = page->new_optgroup(L("Machine frame transforms"), L"param_advanced");
|
auto mf = page->new_optgroup(L("Machine frame transforms"), L"param_advanced");
|
||||||
{
|
{
|
||||||
Line line = { L("Post-gcode axis remap"),
|
Line line = { L("G-code axis remap (post-slice)"), L("Remap slicing-frame axes to machine axes in G-code output. Applied AFTER slicing, during G-code generation.") };
|
||||||
L("Axis remap in the machine-frame stage. Applied AFTER gcode_remap, "
|
line.append_option(mf->get_option("gcode_remap_x"));
|
||||||
"to put coordinates into the printer's physical axis labelling.") };
|
line.append_option(mf->get_option("gcode_remap_y"));
|
||||||
line.append_option(mf->get_option("post_gcode_remap_x"));
|
line.append_option(mf->get_option("gcode_remap_z"));
|
||||||
line.append_option(mf->get_option("post_gcode_remap_y"));
|
|
||||||
line.append_option(mf->get_option("post_gcode_remap_z"));
|
|
||||||
mf->append_line(line);
|
mf->append_line(line);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -5612,20 +5558,9 @@ void TabPrinter::toggle_options()
|
|||||||
// Belt printer: show belt-specific settings only when belt_printer is enabled.
|
// Belt printer: show belt-specific settings only when belt_printer is enabled.
|
||||||
bool is_belt = m_config->opt_bool("belt_printer");
|
bool is_belt = m_config->opt_bool("belt_printer");
|
||||||
bool expert_or_above = (m_mode >= comExpert);
|
bool expert_or_above = (m_mode >= comExpert);
|
||||||
toggle_line("belt_printer_angle", is_belt);
|
|
||||||
toggle_line("belt_printer_infinite_y", is_belt);
|
toggle_line("belt_printer_infinite_y", is_belt);
|
||||||
// Mesh rotate: advanced (visible by default in belt mode).
|
// Belt tilt: the sole mesh-side belt transform (visible by default in belt mode).
|
||||||
toggle_line("belt_slice_rotation", is_belt);
|
toggle_line("belt_slice_rotation", is_belt);
|
||||||
// Mesh shear/scale: expert-only (the rigid rotation above is the
|
|
||||||
// primary belt transform; shear/scale is reserved for power users
|
|
||||||
// matching BlackBelt-style 1/sin scale or asymmetric belt geometries).
|
|
||||||
toggle_line("belt_shear_x", is_belt && expert_or_above);
|
|
||||||
toggle_line("belt_shear_y", is_belt && expert_or_above);
|
|
||||||
toggle_line("belt_shear_z", is_belt && expert_or_above);
|
|
||||||
toggle_line("belt_scale_x", is_belt && expert_or_above);
|
|
||||||
toggle_line("belt_scale_y", is_belt && expert_or_above);
|
|
||||||
toggle_line("belt_scale_z", is_belt && expert_or_above);
|
|
||||||
toggle_line("belt_mesh_transform_order", is_belt && expert_or_above);
|
|
||||||
for (auto el : {"belt_origin_snap_x", "belt_origin_snap_y", "belt_origin_snap_z"})
|
for (auto el : {"belt_origin_snap_x", "belt_origin_snap_y", "belt_origin_snap_z"})
|
||||||
toggle_line(el, is_belt);
|
toggle_line(el, is_belt);
|
||||||
|
|
||||||
@@ -5641,59 +5576,11 @@ void TabPrinter::toggle_options()
|
|||||||
// preslice_remap_global: superseded by belt_preslice_global
|
// preslice_remap_global: superseded by belt_preslice_global
|
||||||
toggle_option("preslice_remap_global", is_belt && !belt_global);
|
toggle_option("preslice_remap_global", is_belt && !belt_global);
|
||||||
|
|
||||||
// Mutual exclusion (UI only): slicing rotation and per-axis shear/scale are
|
// Rotation is the only mesh-side belt transform. Gray out its angle/global
|
||||||
// alternatives in the printer panel. The pipeline math composes them; this
|
// sub-options when no rotation axis is selected.
|
||||||
// just disables the inactive set so users pick one path or the other.
|
|
||||||
auto rot_axis = m_config->option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation")->value;
|
auto rot_axis = m_config->option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation")->value;
|
||||||
bool rotation_active = is_belt
|
toggle_option("belt_slice_rotation_angle", is_belt && rot_axis != BeltRotationAxis::None);
|
||||||
&& rot_axis != BeltRotationAxis::None
|
toggle_option("belt_slice_rotation_global", is_belt && rot_axis != BeltRotationAxis::None);
|
||||||
&& std::abs(m_config->opt_float("belt_slice_rotation_angle")) > 1e-9;
|
|
||||||
bool shear_or_scale_active = is_belt && (
|
|
||||||
m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_x")->value != BeltShearMode::None ||
|
|
||||||
m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_y")->value != BeltShearMode::None ||
|
|
||||||
m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_z")->value != BeltShearMode::None ||
|
|
||||||
m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_x")->value != BeltScaleMode::None ||
|
|
||||||
m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_y")->value != BeltScaleMode::None ||
|
|
||||||
m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_z")->value != BeltScaleMode::None);
|
|
||||||
bool allow_shear_scale = !rotation_active;
|
|
||||||
bool allow_rotation = !shear_or_scale_active;
|
|
||||||
|
|
||||||
// Disable shear/scale mode dropdowns when rotation is active.
|
|
||||||
for (auto el : {"belt_shear_x", "belt_shear_y", "belt_shear_z",
|
|
||||||
"belt_scale_x", "belt_scale_y", "belt_scale_z",
|
|
||||||
"belt_mesh_transform_order"})
|
|
||||||
toggle_option(el, is_belt && allow_shear_scale);
|
|
||||||
|
|
||||||
// Disable rotation controls when any shear/scale is active.
|
|
||||||
toggle_option("belt_slice_rotation", is_belt && allow_rotation);
|
|
||||||
toggle_option("belt_slice_rotation_angle", is_belt && allow_rotation && rot_axis != BeltRotationAxis::None);
|
|
||||||
toggle_option("belt_slice_rotation_global", is_belt && allow_rotation && rot_axis != BeltRotationAxis::None);
|
|
||||||
|
|
||||||
// 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 && allow_shear_scale);
|
|
||||||
toggle_option("belt_shear_x_from", is_belt && sx != BeltShearMode::None && allow_shear_scale);
|
|
||||||
toggle_option("belt_shear_x_global", is_belt && sx != BeltShearMode::None && !belt_global && allow_shear_scale);
|
|
||||||
|
|
||||||
auto sy = m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_y")->value;
|
|
||||||
toggle_option("belt_shear_y_angle", is_belt && sy != BeltShearMode::None && allow_shear_scale);
|
|
||||||
toggle_option("belt_shear_y_from", is_belt && sy != BeltShearMode::None && allow_shear_scale);
|
|
||||||
toggle_option("belt_shear_y_global", is_belt && sy != BeltShearMode::None && !belt_global && allow_shear_scale);
|
|
||||||
|
|
||||||
auto sz = m_config->option<ConfigOptionEnum<BeltShearMode>>("belt_shear_z")->value;
|
|
||||||
toggle_option("belt_shear_z_angle", is_belt && sz != BeltShearMode::None && allow_shear_scale);
|
|
||||||
toggle_option("belt_shear_z_from", is_belt && sz != BeltShearMode::None && allow_shear_scale);
|
|
||||||
toggle_option("belt_shear_z_global", is_belt && sz != BeltShearMode::None && !belt_global && allow_shear_scale);
|
|
||||||
|
|
||||||
auto scx = m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_x")->value;
|
|
||||||
toggle_option("belt_scale_x_angle", is_belt && scx != BeltScaleMode::None && allow_shear_scale);
|
|
||||||
|
|
||||||
auto scy = m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_y")->value;
|
|
||||||
toggle_option("belt_scale_y_angle", is_belt && scy != BeltScaleMode::None && allow_shear_scale);
|
|
||||||
|
|
||||||
auto scz = m_config->option<ConfigOptionEnum<BeltScaleMode>>("belt_scale_z")->value;
|
|
||||||
toggle_option("belt_scale_z_angle", is_belt && scz != BeltScaleMode::None && allow_shear_scale);
|
|
||||||
|
|
||||||
// Machine-frame transforms: shown only in belt mode.
|
// Machine-frame transforms: shown only in belt mode.
|
||||||
// Mirror the Advanced/Expert split used for mesh shear/scale.
|
// Mirror the Advanced/Expert split used for mesh shear/scale.
|
||||||
@@ -5703,8 +5590,7 @@ void TabPrinter::toggle_options()
|
|||||||
toggle_line("gcode_scale_x", is_belt && expert_or_above);
|
toggle_line("gcode_scale_x", is_belt && expert_or_above);
|
||||||
toggle_line("gcode_scale_y", is_belt);
|
toggle_line("gcode_scale_y", is_belt);
|
||||||
toggle_line("gcode_scale_z", is_belt && expert_or_above);
|
toggle_line("gcode_scale_z", is_belt && expert_or_above);
|
||||||
for (auto el : {"belt_gcode_transform_order", "post_gcode_remap_x"})
|
toggle_line("belt_gcode_transform_order", is_belt);
|
||||||
toggle_line(el, is_belt);
|
|
||||||
|
|
||||||
auto gsx = m_config->option<ConfigOptionEnum<BeltShearMode>>("gcode_shear_x")->value;
|
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_angle", is_belt && gsx != BeltShearMode::None);
|
||||||
@@ -5939,21 +5825,29 @@ void TabPrinter::update_fff()
|
|||||||
m_use_silent_mode = m_config->opt_bool("silent_mode");
|
m_use_silent_mode = m_config->opt_bool("silent_mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Belt printer: auto-sync build_plate_tilt_x to belt_printer_angle when belt mode is active.
|
// Belt printer: auto-sync build_plate_tilt_{x,y} (which drives support gravity tilt)
|
||||||
// When belt mode is off, reset build_plate_tilt_x to 0 if it was set by belt mode.
|
// from the belt slicing rotation, the single source of truth for the physical tilt.
|
||||||
|
// Tilt about X drives tilt_x, tilt about Y drives tilt_y. When belt mode is off,
|
||||||
|
// reset whichever tilt axis matches a leftover belt value so we don't clobber a
|
||||||
|
// manually-set tilt on a non-belt tilted printer.
|
||||||
if (m_config->opt_bool("belt_printer")) {
|
if (m_config->opt_bool("belt_printer")) {
|
||||||
double belt_angle = m_config->opt_float("belt_printer_angle");
|
auto rot_axis = m_config->option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation")->value;
|
||||||
if (m_config->opt_float("build_plate_tilt_x") != belt_angle) {
|
const auto tilt = BeltTransformPipeline::physical_tilt(
|
||||||
m_config->set_key_value("build_plate_tilt_x", new ConfigOptionFloat(belt_angle));
|
rot_axis, m_config->opt_float("belt_slice_rotation_angle"));
|
||||||
}
|
if (m_config->opt_float("build_plate_tilt_x") != tilt.tilt_x_deg)
|
||||||
|
m_config->set_key_value("build_plate_tilt_x", new ConfigOptionFloat(tilt.tilt_x_deg));
|
||||||
|
if (m_config->opt_float("build_plate_tilt_y") != tilt.tilt_y_deg)
|
||||||
|
m_config->set_key_value("build_plate_tilt_y", new ConfigOptionFloat(tilt.tilt_y_deg));
|
||||||
} else {
|
} else {
|
||||||
// Only reset if build_plate_tilt_x matches a typical belt angle (was set by auto-sync).
|
const auto tilt = BeltTransformPipeline::physical_tilt(
|
||||||
// Avoid clobbering a manually-set tilt value for non-belt tilted printers.
|
m_config->option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation")->value,
|
||||||
double current_tilt = m_config->opt_float("build_plate_tilt_x");
|
m_config->opt_float("belt_slice_rotation_angle"));
|
||||||
double belt_angle = m_config->opt_float("belt_printer_angle");
|
double tx = m_config->opt_float("build_plate_tilt_x");
|
||||||
if (current_tilt != 0. && std::abs(current_tilt - belt_angle) < 0.01) {
|
double ty = m_config->opt_float("build_plate_tilt_y");
|
||||||
|
if (tx != 0. && std::abs(tx - tilt.tilt_x_deg) < 0.01)
|
||||||
m_config->set_key_value("build_plate_tilt_x", new ConfigOptionFloat(0.));
|
m_config->set_key_value("build_plate_tilt_x", new ConfigOptionFloat(0.));
|
||||||
}
|
if (ty != 0. && std::abs(ty - tilt.tilt_y_deg) < 0.01)
|
||||||
|
m_config->set_key_value("build_plate_tilt_y", new ConfigOptionFloat(0.));
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle_options();
|
toggle_options();
|
||||||
|
|||||||
Reference in New Issue
Block a user