From bf23f6f0231af3c496619240d88b6951baf66ca7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 11 Sep 2023 10:32:37 +0800 Subject: [PATCH] NEW: add TransformationSVD and some apis in Geometry Jira:STUDIO-4227 add TransformationSVD and some apis in Geometry for cut tool these code from PrusaSlice,thanks for enricoturri1966 the original commit message: commit 22ccb5657834e1b2fe548dc647a77044bc9c38f4 Author: enricoturri1966 Date: Thu Feb 2 09:07:03 2023 +0100 Added class TransformationSVD to detect transformation matrix components using singular value decomposition ... Change-Id: I4ce736449029d1fec22def20ef50fa20e1f48713 (cherry picked from commit da9e8127f5214f095da2db9a7898d2e0e84a0214) --- src/libslic3r/Geometry.cpp | 129 +++++++++++++++++++++++++++++++++++-- src/libslic3r/Geometry.hpp | 36 ++++++++++- 2 files changed, 159 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index a2c59ec17..1653f15a2 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -423,6 +423,26 @@ Transform3d rotation_transform(const Vec3d& rotation) return transform; } +void scale_transform(Transform3d &transform, double scale) { + return scale_transform(transform, scale * Vec3d::Ones()); +} + +void scale_transform(Transform3d &transform, const Vec3d &scale) +{ + transform = Transform3d::Identity(); + transform.scale(scale); +} +Transform3d scale_transform(double scale) { + return scale_transform(scale * Vec3d::Ones()); +} + +Transform3d scale_transform(const Vec3d &scale) +{ + Transform3d transform; + scale_transform(transform, scale); + return transform; +} + Transformation::Flags::Flags() : dont_translate(true) , dont_rotate(true) @@ -454,7 +474,11 @@ Transformation::Transformation(const Transform3d& transform) set_from_transform(transform); } -void Transformation::set_offset(const Vec3d& offset) +Transform3d Transformation::get_offset_matrix() const { + return translation_transform(get_offset()); +} + +void Transformation::set_offset(const Vec3d &offset) { set_offset(X, offset(0)); set_offset(Y, offset(1)); @@ -470,7 +494,27 @@ void Transformation::set_offset(Axis axis, double offset) } } -void Transformation::set_rotation(const Vec3d& rotation) +static Transform3d extract_rotation_matrix(const Transform3d &trafo) +{ + Matrix3d rotation; + Matrix3d scale; + trafo.computeRotationScaling(&rotation, &scale); + return Transform3d(rotation); +} + +static Transform3d extract_scale(const Transform3d &trafo) +{ + Matrix3d rotation; + Matrix3d scale; + trafo.computeRotationScaling(&rotation, &scale); + return Transform3d(scale); +} + +Transform3d Transformation::get_rotation_matrix() const { + return extract_rotation_matrix(m_matrix); +} + +void Transformation::set_rotation(const Vec3d &rotation) { set_rotation(X, rotation(0)); set_rotation(Y, rotation(1)); @@ -577,12 +621,31 @@ void Transformation::reset() m_dirty = false; } +void Transformation::reset_rotation() { + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.v * svd.s * svd.v.transpose()) * svd.mirror_matrix(); +} + +void Transformation::reset_scaling_factor() { + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()) * svd.mirror_matrix(); +} + +void Transformation::reset_skew() { + auto new_scale_factor = [](const Matrix3d &s) { + return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average + }; + + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * scale_transform(new_scale_factor(svd.s)) * Transform3d(svd.v.transpose()) * svd.mirror_matrix(); +} + const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const { if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror)) { m_matrix = Geometry::assemble_transform( - dont_translate ? Vec3d::Zero() : m_offset, + dont_translate ? Vec3d::Zero() : m_offset, dont_rotate ? Vec3d::Zero() : m_rotation, dont_scale ? Vec3d::Ones() : m_scaling_factor, dont_mirror ? Vec3d::Ones() : m_mirror @@ -595,6 +658,20 @@ const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rot return m_matrix; } +Transform3d Transformation::get_matrix_no_offset() const +{ + Transformation copy(*this); + copy.reset_offset(); + return copy.get_matrix(); +} + +Transform3d Transformation::get_matrix_no_scaling_factor() const +{ + Transformation copy(*this); + copy.reset_scaling_factor(); + return copy.get_matrix(); +} + Transformation Transformation::operator * (const Transformation& other) const { return Transformation(get_matrix() * other.get_matrix()); @@ -709,4 +786,48 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) return (axis.z() < 0) ? -angle : angle; } -}} // namespace Slic3r::Geometry +Geometry::TransformationSVD::TransformationSVD(const Transform3d &trafo) +{ + const auto &m0 = trafo.matrix().block<3, 3>(0, 0); + mirror = m0.determinant() < 0.0; + + Matrix3d m; + if (mirror) + m = m0 * Eigen::DiagonalMatrix(-1.0, 1.0, 1.0); + else + m = m0; + const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); + u = svd.matrixU(); + v = svd.matrixV(); + s = svd.singularValues().asDiagonal(); + + scale = !s.isApprox(Matrix3d::Identity()); + anisotropic_scale = !is_approx(s(0, 0), s(1, 1)) || !is_approx(s(1, 1), s(2, 2)); + rotation = !v.isApprox(u); + + if (anisotropic_scale) { + rotation_90_degrees = true; + for (int i = 0; i < 3; ++i) { + const Vec3d row = v.row(i).cwiseAbs(); + const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); + const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); + if (num_zeros != 2 || num_ones != 1) { + rotation_90_degrees = false; + break; + } + } + // Detect skew by brute force: check if the axes are still orthogonal after transformation + const Matrix3d trafo_linear = trafo.linear(); + const std::array axes = {Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ()}; + std::array transformed_axes; + for (int i = 0; i < 3; ++i) { transformed_axes[i] = trafo_linear * axes[i]; } + skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON || std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON || + std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON; + + // This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492) + // skew = ! rotation_90_degrees; + } else + skew = false; +} +} // namespace Geometry +} // namespace Slic3r diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 8eb6195a1..8202de271 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -356,6 +356,12 @@ Transform3d translation_transform(const Vec3d &translation); // 2) rotate Y // 3) rotate Z Transform3d rotation_transform(const Vec3d &rotation); +// Sets the given transform by assembling the given scale factors +void scale_transform(Transform3d &transform, double scale); +void scale_transform(Transform3d &transform, const Vec3d &scale); +// Returns the transform obtained by assembling the given scale factors +Transform3d scale_transform(double scale); +Transform3d scale_transform(const Vec3d &scale); class Transformation { @@ -390,13 +396,13 @@ public: const Vec3d& get_offset() const { return m_offset; } double get_offset(Axis axis) const { return m_offset(axis); } - + Transform3d get_offset_matrix() const; void set_offset(const Vec3d& offset); void set_offset(Axis axis, double offset); const Vec3d& get_rotation() const { return m_rotation; } double get_rotation(Axis axis) const { return m_rotation(axis); } - + Transform3d get_rotation_matrix() const; void set_rotation(const Vec3d& rotation); void set_rotation(Axis axis, double rotation); @@ -417,8 +423,15 @@ public: void set_from_transform(const Transform3d& transform); void reset(); + void reset_offset() { set_offset(Vec3d::Zero()); } + void reset_rotation(); + void reset_scaling_factor(); + void reset_mirror() { set_mirror(Vec3d::Ones()); } + void reset_skew(); const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const; + Transform3d get_matrix_no_offset() const; + Transform3d get_matrix_no_scaling_factor() const; Transformation operator * (const Transformation& other) const; @@ -444,6 +457,25 @@ private: } }; +struct TransformationSVD +{ + Matrix3d u{Matrix3d::Identity()}; + Matrix3d s{Matrix3d::Identity()}; + Matrix3d v{Matrix3d::Identity()}; + + bool mirror{false}; + bool scale{false}; + bool anisotropic_scale{false}; + bool rotation{false}; + bool rotation_90_degrees{false}; + bool skew{false}; + + explicit TransformationSVD(const Transformation &trafo) : TransformationSVD(trafo.get_matrix()) {} + explicit TransformationSVD(const Transform3d &trafo); + + Eigen::DiagonalMatrix mirror_matrix() const { return Eigen::DiagonalMatrix(this->mirror ? -1. : 1., 1., 1.); } +}; + // For parsing a transformation matrix from 3MF / AMF. extern Transform3d transform3d_from_string(const std::string& transform_str);