diff --git a/AngleAxis.cpp b/AngleAxis.cpp index 54a5674..1ff8c0e 100644 --- a/AngleAxis.cpp +++ b/AngleAxis.cpp @@ -19,7 +19,7 @@ AngleAxisOf::AngleAxisOf(float angle, DirectionOf axis) { template AngleAxisOf::AngleAxisOf(float angle, Vector3 axis) { this->angle = angle; - this->axis = DirectionOf(axis); + this->axis = DirectionOf::FromVector3(axis); } template @@ -28,7 +28,7 @@ AngleAxisOf::AngleAxisOf(Quaternion q) { Vector3 axis; q.ToAngleAxis(&angle, &axis); this->angle = angle; - this->axis = DirectionOf(axis); + this->axis = DirectionOf::FromVector3(axis); } template diff --git a/CMakeLists.txt b/CMakeLists.txt index d57f009..6c50090 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,21 +40,10 @@ else() enable_testing() - file(GLOB_RECURSE test_srcs test/*.cc) + file(GLOB_RECURSE test_srcs test/*_test.cc) add_executable( LinearAlgebraTest ${test_srcs} - # "test/Angle_test.cc" - # "test/Direction_test.cc" - # "test/DiscreteAngle_test.cc" - # "test/FloatSingle_test.cc" - # "test/Matrix_test.cc" - # "test/Polar_test.cc" - # "test/Quaternion_test.cc" - # "test/Spherical_test.cc" - # "test/Spherical16_test.cc" - # "test/Vector2_test.cc" - # "test/Vector3_test.cc" ) target_link_libraries( LinearAlgebraTest diff --git a/Direction.cpp b/Direction.cpp index e4a26f9..8d83b47 100644 --- a/Direction.cpp +++ b/Direction.cpp @@ -22,17 +22,17 @@ DirectionOf::DirectionOf(AngleOf horizontal, AngleOf vertical) { Normalize(); }; -template -DirectionOf::DirectionOf(Vector3 v) { - this->horizontal = AngleOf::Atan2( - v.Right(), - v.Forward()); // AngleOf::Radians(atan2f(v.Right(), v.Forward())); - this->vertical = - -AngleOf::deg90 - - AngleOf::Acos( - v.Up()); // AngleOf::Radians(-(0.5f * pi) - acosf(v.Up())); - Normalize(); -} +// template +// DirectionOf::DirectionOf(Vector3 v) { +// this->horizontal = AngleOf::Atan2( +// v.Right(), +// v.Forward()); // AngleOf::Radians(atan2f(v.Right(), v.Forward())); +// this->vertical = +// -AngleOf::deg90 - +// AngleOf::Acos( +// v.Up()); // AngleOf::Radians(-(0.5f * pi) - acosf(v.Up())); +// Normalize(); +// } template const DirectionOf DirectionOf::forward = @@ -53,6 +53,28 @@ template const DirectionOf DirectionOf::right = DirectionOf(AngleOf::deg90, AngleOf()); +template +Vector3 Passer::LinearAlgebra::DirectionOf::ToVector3() const { + Quaternion q = Quaternion::Euler(-this->vertical.InDegrees(), + this->horizontal.InDegrees(), 0); + Vector3 v = q * Vector3::forward; + return v; +} + +template +DirectionOf Passer::LinearAlgebra::DirectionOf::FromVector3(Vector3 v) { + DirectionOf d; + d.horizontal = AngleOf::Atan2( + v.Right(), + v.Forward()); // AngleOf::Radians(atan2f(v.Right(), v.Forward())); + d.vertical = + -AngleOf::deg90 - + AngleOf::Acos( + v.Up()); // AngleOf::Radians(-(0.5f * pi) - acosf(v.Up())); + d.Normalize(); + return d; +} + template DirectionOf Passer::LinearAlgebra::DirectionOf::Degrees(float horizontal, float vertical) { diff --git a/Direction.h b/Direction.h index 54faaac..5b5d0fb 100644 --- a/Direction.h +++ b/Direction.h @@ -22,7 +22,10 @@ class DirectionOf { DirectionOf(); DirectionOf(AngleOf horizontal, AngleOf vertical); - DirectionOf(Vector3 v); + // DirectionOf(Vector3 v); + + Vector3 ToVector3() const; + static DirectionOf FromVector3(Vector3 v); static DirectionOf Degrees(float horizontal, float vertical); static DirectionOf Radians(float horizontal, float vertical); diff --git a/Quaternion.cpp b/Quaternion.cpp index 67a761a..1a7352e 100644 --- a/Quaternion.cpp +++ b/Quaternion.cpp @@ -136,7 +136,7 @@ Vector3 Quaternion::operator*(const Vector3& p) const { return result; } -bool Quaternion::operator==(const Quaternion& q) { +bool Quaternion::operator==(const Quaternion& q) const { return (this->x == q.x && this->y == q.y && this->z == q.z && this->w == q.w); } diff --git a/Quaternion.h b/Quaternion.h index cc99a51..1979c76 100644 --- a/Quaternion.h +++ b/Quaternion.h @@ -39,7 +39,7 @@ typedef struct Quat { /// A quaternion /// struct Quaternion : Quat { -public: + public: /// /// Create a new identity quaternion /// @@ -80,7 +80,7 @@ public: /// A unit quaternion /// This will preserve the orientation, /// but ensures that it is a unit quaternion. - static Quaternion Normalize(const Quaternion &q); + static Quaternion Normalize(const Quaternion& q); /// /// Convert to euler angles @@ -88,14 +88,14 @@ public: /// The quaternion to convert /// A vector containing euler angles /// The euler angles performed in the order: Z, X, Y - static Vector3 ToAngles(const Quaternion &q); + static Vector3 ToAngles(const Quaternion& q); /// /// Rotate a vector using this quaterion /// /// The vector to rotate /// The rotated vector - Vector3 operator*(const Vector3 &vector) const; + Vector3 operator*(const Vector3& vector) const; /// /// Multiply this quaternion with another quaternion /// @@ -103,7 +103,7 @@ public: /// The resulting rotation /// The result will be this quaternion rotated according to /// the give rotation. - Quaternion operator*(const Quaternion &rotation) const; + Quaternion operator*(const Quaternion& rotation) const; /// /// Check the equality of two quaternions @@ -114,7 +114,7 @@ public: /// themselves. Two quaternions with the same rotational effect may have /// different components. Use Quaternion::Angle to check if the rotations are /// the same. - bool operator==(const Quaternion &quaternion); + bool operator==(const Quaternion& quaternion) const; /// /// The inverse of quaterion @@ -129,8 +129,8 @@ public: /// The look direction /// The up direction /// The look rotation - static Quaternion LookRotation(const Vector3 &forward, - const Vector3 &upwards); + static Quaternion LookRotation(const Vector3& forward, + const Vector3& upwards); /// /// Creates a quaternion with the given forward direction with up = /// Vector3::up @@ -140,7 +140,7 @@ public: /// For the rotation, Vector::up is used for the up direction. /// Note: if the forward direction == Vector3::up, the result is /// Quaternion::identity - static Quaternion LookRotation(const Vector3 &forward); + static Quaternion LookRotation(const Vector3& forward); /// /// Calculat the rotation from on vector to another @@ -157,7 +157,8 @@ public: /// The destination rotation /// The maximum amount of degrees to /// rotate The possibly limited rotation - static Quaternion RotateTowards(const Quaternion &from, const Quaternion &to, + static Quaternion RotateTowards(const Quaternion& from, + const Quaternion& to, float maxDegreesDelta); /// @@ -166,13 +167,13 @@ public: /// The angle /// The axis /// The resulting quaternion - static Quaternion AngleAxis(float angle, const Vector3 &axis); + static Quaternion AngleAxis(float angle, const Vector3& axis); /// /// Convert this quaternion to angle/axis representation /// /// A pointer to the angle for the result /// A pointer to the axis for the result - void ToAngleAxis(float *angle, Vector3 *axis); + void ToAngleAxis(float* angle, Vector3* axis); /// /// Get the angle between two orientations @@ -190,8 +191,9 @@ public: /// The factor between 0 and 1. /// The resulting rotation /// A factor 0 returns rotation1, factor1 returns rotation2. - static Quaternion Slerp(const Quaternion &rotation1, - const Quaternion &rotation2, float factor); + static Quaternion Slerp(const Quaternion& rotation1, + const Quaternion& rotation2, + float factor); /// /// Unclamped sherical lerp between two rotations /// @@ -201,8 +203,9 @@ public: /// The resulting rotation /// A factor 0 returns rotation1, factor1 returns rotation2. /// Values outside the 0..1 range will result in extrapolated rotations - static Quaternion SlerpUnclamped(const Quaternion &rotation1, - const Quaternion &rotation2, float factor); + static Quaternion SlerpUnclamped(const Quaternion& rotation1, + const Quaternion& rotation2, + float factor); /// /// Create a rotation from euler angles @@ -260,8 +263,10 @@ public: /// A pointer to the quaternion for the swing /// result A pointer to the quaternion for the /// twist result - static void GetSwingTwist(Vector3 axis, Quaternion rotation, - Quaternion *swing, Quaternion *twist); + static void GetSwingTwist(Vector3 axis, + Quaternion rotation, + Quaternion* swing, + Quaternion* twist); /// /// Calculate the dot product of two quaternions @@ -271,20 +276,20 @@ public: /// static float Dot(Quaternion rotation1, Quaternion rotation2); -private: + private: float GetLength() const; float GetLengthSquared() const; - static float GetLengthSquared(const Quaternion &q); + static float GetLengthSquared(const Quaternion& q); - void ToAxisAngleRad(const Quaternion &q, Vector3 *const axis, float *angle); + void ToAxisAngleRad(const Quaternion& q, Vector3* const axis, float* angle); static Quaternion FromEulerRad(Vector3 euler); static Quaternion FromEulerRadXYZ(Vector3 euler); Vector3 xyz() const; }; -} // namespace LinearAlgebra -} // namespace Passer +} // namespace LinearAlgebra +} // namespace Passer using namespace Passer::LinearAlgebra; #endif diff --git a/SwingTwist.cpp b/SwingTwist.cpp index 4d9fc1f..aeba4cc 100644 --- a/SwingTwist.cpp +++ b/SwingTwist.cpp @@ -55,6 +55,26 @@ SwingTwistOf Passer::LinearAlgebra::SwingTwistOf::FromQuaternion( return r; } +template +SphericalOf Passer::LinearAlgebra::SwingTwistOf::ToAngleAxis() const { + Quaternion q = this->ToQuaternion(); + float angle; + Vector3 axis; + q.ToAngleAxis(&angle, &axis); + DirectionOf direction = DirectionOf::FromVector3(axis); + + SphericalOf aa = SphericalOf(angle, direction); + return aa; +} + +template +SwingTwistOf Passer::LinearAlgebra::SwingTwistOf::FromAngleAxis( + SphericalOf aa) { + Vector3 vectorAxis = aa.direction.ToVector3(); + Quaternion q = Quaternion::AngleAxis(aa.distance, vectorAxis); + return SwingTwistOf(); +} + template const SwingTwistOf SwingTwistOf::identity = SwingTwistOf(); diff --git a/SwingTwist.h b/SwingTwist.h index 37cfede..e5b43a4 100644 --- a/SwingTwist.h +++ b/SwingTwist.h @@ -29,6 +29,9 @@ class SwingTwistOf { Quaternion ToQuaternion() const; static SwingTwistOf FromQuaternion(Quaternion q); + SphericalOf ToAngleAxis() const; + static SwingTwistOf FromAngleAxis(SphericalOf aa); + const static SwingTwistOf identity; /// diff --git a/Vector3.cpp b/Vector3.cpp index 958fdaf..e3f1b8e 100644 --- a/Vector3.cpp +++ b/Vector3.cpp @@ -153,7 +153,7 @@ float Vector3::Dot(const Vector3& v1, const Vector3& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } -bool Vector3::operator==(const Vector3& v) { +bool Vector3::operator==(const Vector3& v) const { return (this->x == v.x && this->y == v.y && this->z == v.z); } diff --git a/Vector3.h b/Vector3.h index b480d20..bb3d4b7 100644 --- a/Vector3.h +++ b/Vector3.h @@ -90,7 +90,7 @@ struct Vector3 : Vec3 { /// @return true if it is identical to the given vector /// @note This uses float comparison to check equality which may have strange /// effects. Equality on floats should be avoided. - bool operator==(const Vector3& v); + bool operator==(const Vector3& v) const; /// @brief The vector length /// @param v The vector for which you need the length diff --git a/test/Spherical16_test.cc b/test/Spherical16_test.cc index 4f445c9..2e398e5 100644 --- a/test/Spherical16_test.cc +++ b/test/Spherical16_test.cc @@ -33,6 +33,18 @@ TEST(Spherical16, FromVector3) { EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F) << "s.vert 1 0 0"; } +TEST(Spherical16, Vector3) { + Vector3 v = Vector3(1, 2, 3); + Spherical16 rd = Spherical16::FromVector3(v); + Vector3 rv = rd.ToVector3(); + EXPECT_LT(Vector3::Distance(v, rv), 10e-4) << " 1 2 3 <-> spherical"; + + v = Vector3(1, 2, -3); + rd = Spherical16::FromVector3(v); + rv = rd.ToVector3(); + EXPECT_LT(Vector3::Distance(v, rv), 10e-4) << " 1 2 3 <-> spherical"; +} + // TEST(Spherical16, FromPolar) { // Polar p = Polar(1, 0); // Spherical16 s = Spherical16::FromPolar(p); diff --git a/test/Spherical_test.cc b/test/SphericalSingle_test.cc similarity index 77% rename from test/Spherical_test.cc rename to test/SphericalSingle_test.cc index 69226de..db68c37 100644 --- a/test/Spherical_test.cc +++ b/test/SphericalSingle_test.cc @@ -7,32 +7,32 @@ #define FLOAT_INFINITY std::numeric_limits::infinity() -TEST(Spherical, FromVector3) { +TEST(SphericalSingle, FromVector3) { Vector3 v = Vector3(0, 0, 1); - Spherical s = Spherical::FromVector3(v); + SphericalSingle s = SphericalSingle ::FromVector3(v); EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 0 1"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) << "s.hor 0 0 1"; EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F) << "s.vert 0 0 1"; v = Vector3(0, 1, 0); - s = Spherical::FromVector3(v); + s = SphericalSingle ::FromVector3(v); EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 0 1 0"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) << "s.hor 0 1 0"; EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 90.0F) << "s.vert 0 1 0"; v = Vector3(1, 0, 0); - s = Spherical::FromVector3(v); + s = SphericalSingle ::FromVector3(v); EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance 1 0 0"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 90.0F) << "s.hor 1 0 0"; EXPECT_FLOAT_EQ(s.direction.vertical.InDegrees(), 0.0F) << "s.vert 1 0 0"; } -TEST(Spherical, FromPolar) { +TEST(SphericalSingle, FromPolar) { Polar p = Polar(1, Angle::Degrees(0)); - Spherical s = Spherical::FromPolar(p); + SphericalSingle s = SphericalSingle ::FromPolar(p); EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 0)"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) @@ -41,7 +41,7 @@ TEST(Spherical, FromPolar) { << "s.vert Polar(1 0)"; p = Polar(1, Angle::Degrees(45)); - s = Spherical::FromPolar(p); + s = SphericalSingle ::FromPolar(p); EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 45)"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 45.0F) @@ -50,7 +50,7 @@ TEST(Spherical, FromPolar) { << "s.vert Polar(1 45)"; p = Polar(1, Angle::Degrees(-45)); - s = Spherical::FromPolar(p); + s = SphericalSingle ::FromPolar(p); EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(1 -45)"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), -45.0F) @@ -59,7 +59,7 @@ TEST(Spherical, FromPolar) { << "s.vert Polar(1 -45)"; p = Polar(0, Angle::Degrees(0)); - s = Spherical::FromPolar(p); + s = SphericalSingle ::FromPolar(p); EXPECT_FLOAT_EQ(s.distance, 0.0F) << "s.distance Polar(0 0)"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), 0.0F) @@ -68,7 +68,7 @@ TEST(Spherical, FromPolar) { << "s.vert Polar(0 0)"; p = Polar(-1, Angle::Degrees(0)); - s = Spherical::FromPolar(p); + s = SphericalSingle ::FromPolar(p); EXPECT_FLOAT_EQ(s.distance, 1.0F) << "s.distance Polar(-1 0)"; EXPECT_FLOAT_EQ(s.direction.horizontal.InDegrees(), -180.0F) @@ -77,12 +77,12 @@ TEST(Spherical, FromPolar) { << "s.vert Polar(-1 0)"; } -TEST(Spherical, Incident1) { +TEST(SphericalSingle, Incident1) { Vector3 v = Vector3(2.242557f, 1.027884f, -0.322347f); - Spherical s = Spherical::FromVector3(v); + SphericalSingle s = SphericalSingle ::FromVector3(v); - Spherical sr = - Spherical(2.49F, Angle::Degrees(98.18f), Angle::Degrees(24.4F)); + SphericalSingle sr = + SphericalSingle(2.49F, Angle::Degrees(98.18f), Angle::Degrees(24.4F)); EXPECT_NEAR(s.distance, sr.distance, 1.0e-01); EXPECT_NEAR(s.direction.horizontal.InDegrees(), sr.direction.horizontal.InDegrees(), 1.0e-02); @@ -95,12 +95,12 @@ TEST(Spherical, Incident1) { EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-02) << "toVector3.z 1 0 0"; } -TEST(Spherical, Incident2) { +TEST(SphericalSingle, Incident2) { Vector3 v = Vector3(1.0f, 0.0f, 1.0f); - Spherical s = Spherical::FromVector3(v); + SphericalSingle s = SphericalSingle ::FromVector3(v); - Spherical sr = - Spherical(1.4142135623F, Angle::Degrees(45.0f), Angle::Degrees(0.0F)); + SphericalSingle sr = SphericalSingle(1.4142135623F, Angle::Degrees(45.0f), + Angle::Degrees(0.0F)); EXPECT_NEAR(s.distance, sr.distance, 1.0e-05); EXPECT_NEAR(s.direction.horizontal.InDegrees(), sr.direction.horizontal.InDegrees(), 1.0e-05); @@ -113,9 +113,10 @@ TEST(Spherical, Incident2) { EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06); v = Vector3(0.0f, 1.0f, 1.0f); - s = Spherical::FromVector3(v); + s = SphericalSingle ::FromVector3(v); - sr = Spherical(1.4142135623F, Angle::Degrees(0.0f), Angle::Degrees(45.0F)); + sr = SphericalSingle(1.4142135623F, Angle::Degrees(0.0f), + Angle::Degrees(45.0F)); EXPECT_NEAR(s.distance, sr.distance, 1.0e-05); EXPECT_NEAR(s.direction.horizontal.InDegrees(), sr.direction.horizontal.InDegrees(), 1.0e-05); @@ -128,7 +129,7 @@ TEST(Spherical, Incident2) { EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06); v = Vector3(1.0f, 1.0f, 1.0f); - s = Spherical::FromVector3(v); + s = SphericalSingle ::FromVector3(v); r = Vector3(s); EXPECT_NEAR(s.distance, 1.73205080F, 1.0e-02); @@ -139,17 +140,19 @@ TEST(Spherical, Incident2) { EXPECT_NEAR(r.Up(), v.Up(), 1.0e-06); EXPECT_NEAR(r.Forward(), v.Forward(), 1.0e-06); - // s = Spherical(10, 45, 45); + // s = SphericalSingle + (10, 45, 45); // r = s.ToVector3(); // EXPECT_NEAR(r.x, 5, 1.0e-06); // EXPECT_NEAR(r.y, 7.07, 1.0e-06); // EXPECT_NEAR(r.z, 5, 1.0e-06); } -TEST(Spherical, Addition) { - Spherical v1 = Spherical(1, Angle::Degrees(45), Angle::Degrees(0)); - Spherical v2 = Spherical::zero; - Spherical r = Spherical::zero; +TEST(SphericalSingle, Addition) { + SphericalSingle v1 = + SphericalSingle(1, Angle::Degrees(45), Angle::Degrees(0)); + SphericalSingle v2 = SphericalSingle ::zero; + SphericalSingle r = SphericalSingle ::zero; r = v1 + v2; EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)"; @@ -158,13 +161,13 @@ TEST(Spherical, Addition) { r += v2; EXPECT_FLOAT_EQ(r.distance, v1.distance) << "Addition(0 0 0)"; - v2 = Spherical(1, Angle::Degrees(-45), Angle::Degrees(0)); + v2 = SphericalSingle(1, Angle::Degrees(-45), Angle::Degrees(0)); r = v1 + v2; EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 -45 0)"; EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 0) << "Addition(1 -45 0)"; EXPECT_FLOAT_EQ(r.direction.vertical.InDegrees(), 0) << "Addition(1 -45 0)"; - v2 = Spherical(1, Angle::Degrees(0), Angle::Degrees(90)); + v2 = SphericalSingle(1, Angle::Degrees(0), Angle::Degrees(90)); r = v1 + v2; EXPECT_FLOAT_EQ(r.distance, sqrtf(2)) << "Addition(1 0 90)"; EXPECT_FLOAT_EQ(r.direction.horizontal.InDegrees(), 45) << "Addition(1 0 90)"; diff --git a/test/SwingTwistSingle_test.cc b/test/SwingTwistSingle_test.cc new file mode 100644 index 0000000..49040e3 --- /dev/null +++ b/test/SwingTwistSingle_test.cc @@ -0,0 +1,46 @@ +#if GTEST +#include +#include +#include + +#include "SwingTwist.h" + +#define FLOAT_INFINITY std::numeric_limits::infinity() + +TEST(SwingTwistSingle, Quaternion) { + Quaternion q; + SwingTwistSingle s; + Quaternion rq; + + q = Quaternion::identity; + s = SwingTwistSingle::FromQuaternion(q); + rq = s.ToQuaternion(); + EXPECT_EQ(q, rq) << " 0 0 0 1 <-> SwingTwist"; + + q = Quaternion::Euler(90, 0, 0); + s = SwingTwistSingle::FromQuaternion(q); + rq = s.ToQuaternion(); + EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 90 0 0 <-> SwingTwist"; + + q = Quaternion::Euler(0, 90, 0); + s = SwingTwistSingle::FromQuaternion(q); + rq = s.ToQuaternion(); + EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist"; + + q = Quaternion::Euler(0, 0, 90); + s = SwingTwistSingle::FromQuaternion(q); + rq = s.ToQuaternion(); + EXPECT_EQ(q, rq) << " Euler 0 0 90 <-> SwingTwist"; + + q = Quaternion::Euler(0, 180, 0); // ==> spherical S(180 0)T0 + s = SwingTwistSingle::FromQuaternion(q); + rq = s.ToQuaternion(); + EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist"; + + q = Quaternion::Euler(0, 135, 0); // ==> spherical S(180 45)T0 + s = SwingTwistSingle::FromQuaternion(q); + rq = s.ToQuaternion(); + EXPECT_LT(Quaternion::Angle(q, rq), 10e-2) << " Euler 0 90 0 <-> SwingTwist"; +} + +#endif \ No newline at end of file