From 5b243eec9245703510b283a9465675c691228dbe Mon Sep 17 00:00:00 2001 From: Joseph Robertson <32232925+HarrierPigeon@users.noreply.github.com> Date: Mon, 8 Jun 2026 06:58:26 -0500 Subject: [PATCH] Relocate Pre-Slice remap logic --- src/libslic3r/BeltSliceStrategy.cpp | 212 ++++++++++++++-------------- src/libslic3r/BeltSliceStrategy.hpp | 43 +++--- src/libslic3r/PrintObjectSlice.cpp | 35 +---- 3 files changed, 132 insertions(+), 158 deletions(-) diff --git a/src/libslic3r/BeltSliceStrategy.cpp b/src/libslic3r/BeltSliceStrategy.cpp index fd1bfcc191..cdb6ba428f 100644 --- a/src/libslic3r/BeltSliceStrategy.cpp +++ b/src/libslic3r/BeltSliceStrategy.cpp @@ -1,6 +1,8 @@ #include "BeltSliceStrategy.hpp" #include "Model.hpp" +#include + #include #ifdef SLIC3R_BELT_DIAGNOSTIC_LOG #include @@ -10,130 +12,132 @@ namespace Slic3r { -std::unique_ptr BeltSliceStrategy::create(const PrintConfig &config) +void BeltSliceStrategy::apply_preslice_transforms(Transform3d &trafo, + const PrintConfig &config, + const ModelVolumePtrs &model_volumes, + double *out_belt_min_z) { - if (!config.belt_printer.value) - return nullptr; - return std::unique_ptr(new BeltSliceStrategy(config)); -} + // 1. Standalone pre-slice axis remap (works without belt mode). + const bool has_remap = BeltTransformPipeline::has_preslice_remap(config); + if (has_remap) + trafo = BeltTransformPipeline::build_preslice_remap(config) * trafo; -BeltSliceStrategy::BeltSliceStrategy(const PrintConfig &config) -{ - m_rotation = BeltTransformPipeline::build_rotation_matrix(config, &m_has_rotation); -} - -void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo, - const ModelVolumePtrs &model_volumes, - bool has_remap, - double *out_belt_min_z) const -{ - // Rotation is the only mesh-side belt transform (matching build_forward_transform). - if (m_has_rotation) { - Transform3d belt_xform = Transform3d::Identity(); - belt_xform.linear() = m_rotation; - trafo = belt_xform * trafo; + // 2. Belt rotation — the sole mesh-side belt transform (matching + // BeltTransformPipeline::build_forward_transform). Only active in + // belt-printer mode. + bool has_rotation = false; + if (config.belt_printer.value) { + const Matrix3d rot = BeltTransformPipeline::build_rotation_matrix(config, &has_rotation); + if (has_rotation) { + Transform3d belt_xform = Transform3d::Identity(); + belt_xform.linear() = rot; + trafo = belt_xform * trafo; + } } - // Z-shift — detect if mesh clips below build plate after transforms. - // Each mesh vertex must be brought into object space via mv->get_matrix() - // before applying the full trafo (which is in object space). Missing this - // step on assemblies (where per-volume get_matrix() positions each volume - // within the object) causes min_z to be computed against mesh-local vertex - // coordinates rather than object-space coordinates, so volumes translated - // along the slicer's Z axis are silently excluded from the bound check. - if (has_remap || m_has_rotation) { + if (!has_remap && !has_rotation) + return; + + // 3. Z-shift — detect if the mesh clips below the build plate after the + // transforms and lift it. Each mesh vertex must be brought into object space + // via mv->get_matrix() before applying the full trafo (which is in object + // space). Missing this on assemblies (where per-volume get_matrix() positions + // each volume within the object) would compute min_z against mesh-local vertex + // coordinates rather than object-space coordinates, so volumes translated along + // the slicer's Z axis would be silently excluded from the bound check. #ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - // Capture the incoming trafo for diagnostic logging. - // This is the slicer-frame transform AFTER belt_xform but BEFORE z_shift. - const Transform3d trafo_pre_shift = trafo; - auto log_mat = [](const Matrix3d &m) { - std::ostringstream ss; - ss << std::fixed << std::setprecision(4); - ss << "[[" << m(0,0) << "," << m(0,1) << "," << m(0,2) << "]," - << "[" << m(1,0) << "," << m(1,1) << "," << m(1,2) << "]," - << "[" << m(2,0) << "," << m(2,1) << "," << m(2,2) << "]]"; - return ss.str(); - }; - auto log_vec3 = [](const Vec3d &v) { - std::ostringstream ss; - ss << std::fixed << std::setprecision(4); - ss << "(" << v.x() << "," << v.y() << "," << v.z() << ")"; - return ss.str(); - }; - BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_to_trafo enter" - << " has_rotation=" << m_has_rotation - << " has_remap=" << has_remap - << " trafo.linear=" << log_mat(trafo_pre_shift.linear()) - << " trafo.translation=" << log_vec3(trafo_pre_shift.translation()) - << " volumes=" << model_volumes.size(); + // Capture the incoming trafo for diagnostic logging. + // This is the slicer-frame transform AFTER remap + rotation but BEFORE z_shift. + const Transform3d trafo_pre_shift = trafo; + auto log_mat = [](const Matrix3d &m) { + std::ostringstream ss; + ss << std::fixed << std::setprecision(4); + ss << "[[" << m(0,0) << "," << m(0,1) << "," << m(0,2) << "]," + << "[" << m(1,0) << "," << m(1,1) << "," << m(1,2) << "]," + << "[" << m(2,0) << "," << m(2,1) << "," << m(2,2) << "]]"; + return ss.str(); + }; + auto log_vec3 = [](const Vec3d &v) { + std::ostringstream ss; + ss << std::fixed << std::setprecision(4); + ss << "(" << v.x() << "," << v.y() << "," << v.z() << ")"; + return ss.str(); + }; + BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_preslice_transforms enter" + << " has_rotation=" << has_rotation + << " has_remap=" << has_remap + << " trafo.linear=" << log_mat(trafo_pre_shift.linear()) + << " trafo.translation=" << log_vec3(trafo_pre_shift.translation()) + << " volumes=" << model_volumes.size(); #endif - double min_z = std::numeric_limits::max(); + double min_z = std::numeric_limits::max(); #ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - int vol_idx = 0; + int vol_idx = 0; #endif - for (const ModelVolume *mv : model_volumes) { + for (const ModelVolume *mv : model_volumes) { #ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - if (!mv->is_model_part()) { ++vol_idx; continue; } + if (!mv->is_model_part()) { ++vol_idx; continue; } #else - if (!mv->is_model_part()) continue; + if (!mv->is_model_part()) continue; #endif - Transform3d vol_trafo = trafo * mv->get_matrix(); - const auto &its = mv->mesh().its; + Transform3d vol_trafo = trafo * mv->get_matrix(); + const auto &its = mv->mesh().its; #ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - // Per-volume bbox in mesh-frame and post-trafo slicer-frame. - Vec3d mesh_min(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); - Vec3d mesh_max(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()); - Vec3d slicer_min(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); - Vec3d slicer_max(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()); - double vol_min_z = std::numeric_limits::max(); + // Per-volume bbox in mesh-frame and post-trafo slicer-frame. + Vec3d mesh_min(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); + Vec3d mesh_max(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()); + Vec3d slicer_min(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); + Vec3d slicer_max(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest()); + double vol_min_z = std::numeric_limits::max(); #endif - for (const stl_vertex &v : its.vertices) { - Vec3d vm = v.cast(); - Vec3d pt = vol_trafo * vm; - min_z = std::min(min_z, pt.z()); + for (const stl_vertex &v : its.vertices) { + Vec3d vm = v.cast(); + Vec3d pt = vol_trafo * vm; + min_z = std::min(min_z, pt.z()); #ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - mesh_min = mesh_min.cwiseMin(vm); - mesh_max = mesh_max.cwiseMax(vm); - slicer_min = slicer_min.cwiseMin(pt); - slicer_max = slicer_max.cwiseMax(pt); - vol_min_z = std::min(vol_min_z, pt.z()); -#endif - } -#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] vol[" << vol_idx - << "] id=" << mv->id().id << " name='" << mv->name << "'" - << " mesh_bbox_min=" << log_vec3(mesh_min) << " mesh_bbox_max=" << log_vec3(mesh_max) - << " get_matrix.translation=" << log_vec3(mv->get_matrix().translation()) - << " slicer_bbox_min=" << log_vec3(slicer_min) << " slicer_bbox_max=" << log_vec3(slicer_max) - << " vol_min_z=" << vol_min_z; - ++vol_idx; + mesh_min = mesh_min.cwiseMin(vm); + mesh_max = mesh_max.cwiseMax(vm); + slicer_min = slicer_min.cwiseMin(pt); + slicer_max = slicer_max.cwiseMax(pt); + vol_min_z = std::min(vol_min_z, pt.z()); #endif } - double belt_z_shift_val = (min_z < 0. && min_z != std::numeric_limits::max()) ? -min_z : 0.; #ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] combined min_z=" << min_z - << " z_shift_val=" << belt_z_shift_val; -#endif - if (belt_z_shift_val > 0.) { - Transform3d z_shift = Transform3d::Identity(); - z_shift.matrix()(2, 3) = belt_z_shift_val; - trafo = z_shift * trafo; - } - if (out_belt_min_z) { - double new_val = (min_z != std::numeric_limits::max()) ? min_z : 0.; -#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - 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; -#endif - *out_belt_min_z = new_val; - } -#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG - BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_to_trafo exit" - << " final_trafo.linear=" << log_mat(trafo.linear()) - << " final_trafo.translation=" << log_vec3(trafo.translation()); + BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] vol[" << vol_idx + << "] id=" << mv->id().id << " name='" << mv->name << "'" + << " mesh_bbox_min=" << log_vec3(mesh_min) << " mesh_bbox_max=" << log_vec3(mesh_max) + << " get_matrix.translation=" << log_vec3(mv->get_matrix().translation()) + << " slicer_bbox_min=" << log_vec3(slicer_min) << " slicer_bbox_max=" << log_vec3(slicer_max) + << " vol_min_z=" << vol_min_z; + ++vol_idx; #endif } + const double z_shift_val = (min_z < 0. && min_z != std::numeric_limits::max()) ? -min_z : 0.; +#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG + BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] combined min_z=" << min_z + << " z_shift_val=" << z_shift_val; +#endif + if (z_shift_val > 0.) { + Transform3d z_shift = Transform3d::Identity(); + z_shift.matrix()(2, 3) = z_shift_val; + trafo = z_shift * trafo; + } + // out_belt_min_z is only meaningful in belt mode; the standalone-remap path + // never reported it. + if (out_belt_min_z && config.belt_printer.value) { + const double new_val = (min_z != std::numeric_limits::max()) ? min_z : 0.; +#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG + 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; +#endif + *out_belt_min_z = new_val; + } +#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG + BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_preslice_transforms exit" + << " final_trafo.linear=" << log_mat(trafo.linear()) + << " final_trafo.translation=" << log_vec3(trafo.translation()); +#endif } } // namespace Slic3r diff --git a/src/libslic3r/BeltSliceStrategy.hpp b/src/libslic3r/BeltSliceStrategy.hpp index ff56f6fece..33a7ed4979 100644 --- a/src/libslic3r/BeltSliceStrategy.hpp +++ b/src/libslic3r/BeltSliceStrategy.hpp @@ -6,36 +6,31 @@ #include "PrintConfig.hpp" #include "Model.hpp" -#include -#include - namespace Slic3r { -// Belt printer pre-slice transform strategy. +// Belt printer / pre-slice transform strategy. // -// Encapsulates the pre-remap, rotation, and Z-shift transforms that are -// applied to model geometry before slicing on belt printers. (Shear & scale -// are applied to the g-code, not the mesh.) Used by PrintObjectSlice.cpp to -// isolate belt-specific logic from the slicing pipeline. +// Composes, in order, the pre-slice mesh transforms applied before slicing: +// 1. Pre-slice axis remap (standalone — works without belt mode) +// 2. Belt rotation (the sole mesh-side belt transform; shear & scale are a +// g-code-side stage, see MachineFrameTransform) +// 3. Per-object Z-shift that lifts the mesh above the build plate +// +// Isolates this belt/remap-specific logic from the generic slicing pipeline in +// PrintObjectSlice.cpp. class BeltSliceStrategy { public: - // Create a strategy if belt_printer is enabled; returns nullptr otherwise. - static std::unique_ptr create(const PrintConfig &config); - - // Apply belt-specific transforms (rotation + z-shift) to the slicing trafo. - // Pre-slice remap is handled separately (standalone feature). - // has_remap: whether pre-slice remap was already applied (affects z-shift detection). - void apply_to_trafo(Transform3d &trafo, - const ModelVolumePtrs &model_volumes, - bool has_remap, - double *out_belt_min_z) const; - -private: - explicit BeltSliceStrategy(const PrintConfig &config); - - bool m_has_rotation = false; - Matrix3d m_rotation = Matrix3d::Identity(); + // Apply the pre-slice remap + belt rotation + Z-shift to `trafo` in place. + // No-op when neither a remap nor a belt rotation is configured. + // + // out_belt_min_z (if non-null) receives the minimum mesh Z after the + // transforms, but only in belt-printer mode — the standalone-remap path + // never reported it. + static void apply_preslice_transforms(Transform3d &trafo, + const PrintConfig &config, + const ModelVolumePtrs &model_volumes, + double *out_belt_min_z = nullptr); }; } // namespace Slic3r diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 448b39fe0b..7e5203d624 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -173,35 +173,10 @@ static std::vector slice_volumes_inner( params_base.closing_radius = print_object_config.slice_closing_radius.value; params_base.extra_offset = 0; params_base.trafo = object_trafo; - // Standalone pre-slice axis remap (works without belt mode). - bool has_remap = BeltTransformPipeline::has_preslice_remap(print_config); - if (has_remap) - params_base.trafo = BeltTransformPipeline::build_preslice_remap(print_config) * params_base.trafo; - { - // Belt-specific transforms (shear, scale, z-shift) via strategy. - auto belt_strategy = BeltSliceStrategy::create(print_config); - if (belt_strategy) - belt_strategy->apply_to_trafo(params_base.trafo, model_volumes, has_remap, out_belt_min_z); - } - // If only remap (no belt), still need z-shift detection. - // Compose per-volume mv->get_matrix() so assembly volumes contribute their - // actual object-space positions — see the matching note in BeltSliceStrategy. - if (has_remap && !print_config.belt_printer.value) { - double min_z = std::numeric_limits::max(); - for (const ModelVolume *mv : model_volumes) { - if (!mv->is_model_part()) continue; - Transform3d vol_trafo = params_base.trafo * mv->get_matrix(); - for (const stl_vertex &v : mv->mesh().its.vertices) { - Vec3d pt = vol_trafo * v.cast(); - min_z = std::min(min_z, pt.z()); - } - } - if (min_z < 0. && min_z != std::numeric_limits::max()) { - Transform3d z_shift = Transform3d::Identity(); - z_shift.matrix()(2, 3) = -min_z; - params_base.trafo = z_shift * params_base.trafo; - } - } + // Pre-slice mesh transforms: axis remap (standalone — works without belt + // mode), belt rotation, and the per-object Z-shift. Owned by BeltSliceStrategy + // so this belt/remap-specific logic stays out of the generic slicing pipeline. + BeltSliceStrategy::apply_preslice_transforms(params_base.trafo, print_config, model_volumes, out_belt_min_z); //BBS: 0.0025mm is safe enough to simplify the data to speed slicing up for high-resolution model. //Also has on influence on arc fitting which has default resolution 0.0125mm. params_base.resolution = print_config.resolution <= 0.001 ? 0.0f : 0.0025; @@ -981,7 +956,7 @@ void PrintObject::slice() // Per-object Z-shift compensation, applied regardless of global mode. // - // BeltSliceStrategy::apply_to_trafo lifts the mesh by max(0, -m_belt_min_z) + // BeltSliceStrategy::apply_preslice_transforms lifts the mesh by max(0, -m_belt_min_z) // so the slicer can slice with slicer_z >= 0. BeltBackTransform inverts // build_forward_transform() which DOES NOT include this per-object // Z-shift (it's not known until vertex scan time). Result: G-code