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 <enricoturri@seznam.cz>
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)
This commit is contained in:
enricoturri1966 2023-09-11 10:32:37 +08:00 committed by Lane.Wei
parent 487c86e915
commit bf23f6f023
2 changed files with 159 additions and 6 deletions

View File

@ -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<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> 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<Vec3d, 3> axes = {Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ()};
std::array<Vec3d, 3> 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

View File

@ -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<double, 3, 3> mirror_matrix() const { return Eigen::DiagonalMatrix<double, 3, 3>(this->mirror ? -1. : 1., 1., 1.); }
};
// For parsing a transformation matrix from 3MF / AMF.
extern Transform3d transform3d_from_string(const std::string& transform_str);