From: Mikko Rasa Date: Sun, 26 Jan 2025 15:09:52 +0000 (+0200) Subject: Add a function to convert a matrix into a quaternion X-Git-Url: https://git.tdb.fi/?a=commitdiff_plain;h=b6468cb3cab2253294bde436cbf753ea08aead54;p=libs%2Fmath.git Add a function to convert a matrix into a quaternion Currently it only accepts orthonormal matrices. --- diff --git a/source/geometry/quaternion.h b/source/geometry/quaternion.h index f94e63b..2e15c1a 100644 --- a/source/geometry/quaternion.h +++ b/source/geometry/quaternion.h @@ -23,6 +23,7 @@ struct Quaternion static Quaternion one() { return Quaternion(1, 0, 0, 0); } static Quaternion rotation(Angle, const LinAl::Vector &); static Quaternion rotation(const LinAl::Vector &, const LinAl::Vector &); + static Quaternion from_matrix(const LinAl::Matrix &); T scalar() const { return a; } LinAl::Vector vector() const { return LinAl::Vector(b, c, d); } @@ -63,6 +64,44 @@ Quaternion Quaternion::rotation(const LinAl::Vector &from, const Lin return Quaternion(c+sqrt(inner_product(axis, axis)+c*c), axis.x, axis.y, axis.z).normalize(); } +template +Quaternion Quaternion::from_matrix(const LinAl::Matrix &matrix) +{ + if(!is_orthonormal(matrix)) + throw std::domain_error("Quaternion::from_matrix"); + + using std::sqrt; + + T xx = matrix(0, 0); + T yy = matrix(1, 1); + T zz = matrix(2, 2); + T t = xx+yy+zz; + if(t>T()) + { + T r = sqrt(T(1)+t); + T s = T(1)/(T(2)*r); // Equal to 1/(4*a) + return Quaternion(r/T(2), (matrix(2, 1)-matrix(1, 2))*s, (matrix(0, 2)-matrix(2, 0))*s, (matrix(1, 0)-matrix(0, 1))*s); + } + else if(xx>yy && xx>zz) + { + T r = sqrt(T(1)+xx-yy-zz); + T s = T(1)/(T(2)*r); // Equal to 1/(4*b) + return Quaternion((matrix(2, 1)-matrix(1, 2))*s, r/T(2), (matrix(0, 1)+matrix(1, 0))*s, (matrix(0, 2)+matrix(2, 0))*s); + } + else if(yy>zz) + { + T r = sqrt(T(1)-xx+yy-zz); + T s = T(1)/(T(2)*r); // Equal to 1/(4*c) + return Quaternion((matrix(0, 2)-matrix(2, 0))*s, (matrix(0, 1)+matrix(1, 0))*s, r/T(2), (matrix(1, 2)+matrix(2, 1))*s); + } + else + { + T r = sqrt(T(1)-xx-yy+zz); + T s = T(1)/(T(2)*r); // Equal to 1/(4*d) + return Quaternion((matrix(1, 0)-matrix(0, 1))*s, (matrix(0, 2)+matrix(2, 0))*s, (matrix(1, 2)+matrix(2, 1))*s, r/T(2)); + } +} + template Quaternion &Quaternion::operator+=(const Quaternion &q) { diff --git a/source/linal/matrix.h b/source/linal/matrix.h index 9f13ae9..4811a56 100644 --- a/source/linal/matrix.h +++ b/source/linal/matrix.h @@ -300,6 +300,31 @@ inline Matrix transpose(const Matrix &m) return r; } +template +inline bool is_orthonormal(const Matrix &m, T epsilon = std::numeric_limits::epsilon()*4) +{ + using std::abs; + for(unsigned j=0; jepsilon*T(2)) + return false; + + for(unsigned k=0; kepsilon) + return false; + } + } + + return true; +} + template inline std::ostream &operator<<(std::ostream &s, const Matrix &m) {