From a49bb0d71816ef2f23c141e59e549345255815c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 16 Feb 2013 22:32:43 +0100 Subject: [PATCH] Math: convenience functions for transforming vectors with matrices. It's now possible to conveniently transform 2D vectors and points with 3x3 matrices and 3D vectors/points with 4x4 matrices. Previous most low-level solution: Matrix4 m; Vector3 v; Vector3 a = (m*Vector4(v, 1.0f)).xyz(); Vector4 b = (m*Vector4(v, 0.0f)).xyz(); Another, more generalized solution for points was with Point2D/Point3D, adding a lot of confusion (what is that class and what does .vector()?): Vector3 a = (m*Point3D(v)).vector(); And the worst solution was with generic 2D/3D code (WTF!): auto a = (m*typename DimensionTraits::PointType(v)).vector(); Now it is just this, similar for both dimensions: Vector3 a = m.transformPoint(v); Vector3 b = m.transformVector(v); Note that transformation three-component vectors with 3x3 matrices or four-component vectors with 4x4 matrices is easy enough so it doesn't need any special convenience functions whatsoever: Vector3 c = m.rotation()*v; --- src/Math/DualQuaternion.h | 2 +- src/Math/Matrix3.h | 24 ++++++++++++++++++++++++ src/Math/Matrix4.h | 25 +++++++++++++++++++++++++ src/Math/Quaternion.h | 4 ++-- src/Math/Test/DualQuaternionTest.cpp | 4 ++-- src/Math/Test/Matrix3Test.cpp | 14 +++++++++++++- src/Math/Test/Matrix4Test.cpp | 14 +++++++++++++- src/Math/Test/QuaternionTest.cpp | 4 ++-- src/Physics/AxisAlignedBox.cpp | 4 ++-- src/Physics/Capsule.cpp | 4 ++-- src/Physics/Line.cpp | 4 ++-- src/Physics/Plane.cpp | 2 +- src/Physics/Point.cpp | 2 +- src/Physics/Sphere.cpp | 2 +- 14 files changed, 91 insertions(+), 18 deletions(-) diff --git a/src/Math/DualQuaternion.h b/src/Math/DualQuaternion.h index e65df7dfb..d36d2e495 100644 --- a/src/Math/DualQuaternion.h +++ b/src/Math/DualQuaternion.h @@ -218,7 +218,7 @@ template class DualQuaternion: public Dual> { * Expects that the dual quaternion is normalized. @f[ * v' = qv \overline{\hat q^*} = q ([\boldsymbol 0, 1] + \epsilon [\boldsymbol v, 0]) \overline{\hat q^*} * @f] - * @see Quaternion::rotateVectorNormalized() + * @see Matrix4::transformPoint(), Quaternion::rotateVectorNormalized() */ inline Vector3 transformPointNormalized(const Vector3& vector) const { CORRADE_ASSERT(MathTypeTraits>::equals(norm(), Dual(1)), diff --git a/src/Math/Matrix3.h b/src/Math/Matrix3.h index c209aabae..e76bbf8f2 100644 --- a/src/Math/Matrix3.h +++ b/src/Math/Matrix3.h @@ -204,6 +204,30 @@ template class Matrix3: public Matrix<3, T> { return from(inverseRotation, inverseRotation*-translation()); } + /** + * @brief Transform 2D vector with the matrix + * + * Translation is not involved in the transformation. @f[ + * \boldsymbol v' = \boldsymbol M (v_x, v_y, 0)^T + * @f] + * @see transformPoint(), Matrix4::transformVector() + */ + inline Vector2 transformVector(const Vector2& vector) const { + return ((*this)*Vector3(vector, T(0))).xy(); + } + + /** + * @brief Transform 2D point with the matrix + * + * Unlike in transformVector(), translation is also involved. @f[ + * \boldsymbol v' = \boldsymbol M (v_x, v_y, 1)^T + * @f] + * @see Matrix4::transformPoint() + */ + inline Vector2 transformPoint(const Vector2& vector) const { + return ((*this)*Vector3(vector, T(1))).xy(); + } + #ifndef DOXYGEN_GENERATING_OUTPUT inline Point2D operator*(const Point2D& other) const { return Matrix<3, T>::operator*(other); diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index 4b5c85ce1..da1a0d9be 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -348,6 +348,31 @@ template class Matrix4: public Matrix<4, T> { return from(inverseRotation, inverseRotation*-translation()); } + /** + * @brief Transform 3D vector with the matrix + * + * Translation is not involved in the transformation. @f[ + * \boldsymbol v' = \boldsymbol M (v_x, v_y, v_z, 0)^T + * @f] + * @see transformPoint(), Quaternion::rotateVector(), + * Matrix3::transformVector() + */ + inline Vector3 transformVector(const Vector3& vector) const { + return ((*this)*Vector4(vector, T(0))).xyz(); + } + + /** + * @brief Transform 3D point with the matrix + * + * Unlike in transformVector(), translation is also involved. @f[ + * \boldsymbol v' = \boldsymbol M (v_x, v_y, v_z, 1)^T + * @f] + * @see DualQuaternion::transformPointNormalized(), Matrix3::transformPoint() + */ + inline Vector3 transformPoint(const Vector3& vector) const { + return ((*this)*Vector4(vector, T(1))).xyz(); + } + #ifndef DOXYGEN_GENERATING_OUTPUT inline Point3D operator*(const Point3D& other) const { return Matrix<4, T>::operator*(other); diff --git a/src/Math/Quaternion.h b/src/Math/Quaternion.h index 3d57aa838..24245c969 100644 --- a/src/Math/Quaternion.h +++ b/src/Math/Quaternion.h @@ -403,7 +403,7 @@ template class Quaternion { * quaternions. @f[ * v' = qvq^{-1} = q [\boldsymbol v, 0] q^{-1} * @f] - * @see DualQuaternion::transformPointNormalized() + * @see Matrix4::transformVector(), DualQuaternion::transformPointNormalized() */ inline Vector3 rotateVector(const Vector3& vector) const { return ((*this)*Quaternion(vector)*inverted()).vector(); @@ -416,7 +416,7 @@ template class Quaternion { * normalized. @f[ * v' = qvq^{-1} = qvq^* = q [\boldsymbol v, 0] q^* * @f] - * @see DualQuaternion::transformPointNormalized() + * @see Matrix4::transformVector(), DualQuaternion::transformPointNormalized() */ inline Vector3 rotateVectorNormalized(const Vector3& vector) const { CORRADE_ASSERT(MathTypeTraits::equals(dot(), T(1)), diff --git a/src/Math/Test/DualQuaternionTest.cpp b/src/Math/Test/DualQuaternionTest.cpp index 382586469..896ab5c2a 100644 --- a/src/Math/Test/DualQuaternionTest.cpp +++ b/src/Math/Test/DualQuaternionTest.cpp @@ -157,11 +157,11 @@ void DualQuaternionTest::transformPointNormalized() { CORRADE_COMPARE(o.str(), "Math::DualQuaternion::transformPointNormalized(): dual quaternion must be normalized\n"); Vector3 transformedA = a.transformPointNormalized(v); - CORRADE_COMPARE(transformedA, (m*Vector4(v, 1.0f)).xyz()); + CORRADE_COMPARE(transformedA, m.transformPoint(v)); CORRADE_COMPARE(transformedA, Vector3(-1.0f, -1.58733f, 2.237721f)); Vector3 transformedB = b.transformPointNormalized(v); - CORRADE_COMPARE(transformedB, (n*Vector4(v, 1.0f)).xyz()); + CORRADE_COMPARE(transformedB, n.transformPoint(v)); CORRADE_COMPARE(transformedB, Vector3(-1.0f, -2.918512f, 2.780698f)); } diff --git a/src/Math/Test/Matrix3Test.cpp b/src/Math/Test/Matrix3Test.cpp index e5e57e11c..5f61137ca 100644 --- a/src/Math/Test/Matrix3Test.cpp +++ b/src/Math/Test/Matrix3Test.cpp @@ -38,6 +38,7 @@ class Matrix3Test: public Corrade::TestSuite::Tester { void rotationPart(); void vectorParts(); void invertedEuclidean(); + void transform(); void debug(); void configuration(); @@ -50,6 +51,7 @@ typedef Math::Point2D Point2D; Matrix3Test::Matrix3Test() { addTests(&Matrix3Test::constructIdentity, + &Matrix3Test::translation, &Matrix3Test::scaling, &Matrix3Test::rotation, @@ -60,6 +62,8 @@ Matrix3Test::Matrix3Test() { &Matrix3Test::rotationPart, &Matrix3Test::vectorParts, &Matrix3Test::invertedEuclidean, + &Matrix3Test::transform, + &Matrix3Test::debug, &Matrix3Test::configuration); } @@ -121,7 +125,7 @@ void Matrix3Test::reflection() { {0.0f, 0.0f, 1.0f}); CORRADE_COMPARE(actual*actual, Matrix3()); - CORRADE_COMPARE((actual*Point2D(normal)).vector(), -normal); + CORRADE_COMPARE(actual.transformVector(normal), -normal); CORRADE_COMPARE(actual, expected); } @@ -203,6 +207,14 @@ void Matrix3Test::invertedEuclidean() { CORRADE_COMPARE(actual.invertedEuclidean(), actual.inverted()); } +void Matrix3Test::transform() { + Matrix3 a = Matrix3::translation({1.0f, -5.0f})*Matrix3::rotation(deg(90.0f)); + Vector2 v(1.0f, -2.0f); + + CORRADE_COMPARE(a.transformVector(v), Vector2(2.0f, 1.0f)); + CORRADE_COMPARE(a.transformPoint(v), Vector2(3.0f, -4.0f)); +} + void Matrix3Test::debug() { Matrix3 m({3.0f, 5.0f, 8.0f}, {4.0f, 4.0f, 7.0f}, diff --git a/src/Math/Test/Matrix4Test.cpp b/src/Math/Test/Matrix4Test.cpp index 24c0debd2..b9c68eee3 100644 --- a/src/Math/Test/Matrix4Test.cpp +++ b/src/Math/Test/Matrix4Test.cpp @@ -43,6 +43,7 @@ class Matrix4Test: public Corrade::TestSuite::Tester { void rotationPart(); void vectorParts(); void invertedEuclidean(); + void transform(); void debug(); void configuration(); @@ -55,6 +56,7 @@ typedef Math::Point3D Point3D; Matrix4Test::Matrix4Test() { addTests(&Matrix4Test::constructIdentity, + &Matrix4Test::translation, &Matrix4Test::scaling, &Matrix4Test::rotation, @@ -70,6 +72,8 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::rotationPart, &Matrix4Test::vectorParts, &Matrix4Test::invertedEuclidean, + &Matrix4Test::transform, + &Matrix4Test::debug, &Matrix4Test::configuration); } @@ -169,7 +173,7 @@ void Matrix4Test::reflection() { { 0.0f, 0.0f, 0.0f, 1.0f}); CORRADE_COMPARE(actual*actual, Matrix4()); - CORRADE_COMPARE((actual*Point3D(normal)).vector(), -normal); + CORRADE_COMPARE(actual.transformVector(normal), -normal); CORRADE_COMPARE(actual, expected); } @@ -277,6 +281,14 @@ void Matrix4Test::invertedEuclidean() { CORRADE_COMPARE(actual.invertedEuclidean(), actual.inverted()); } +void Matrix4Test::transform() { + Matrix4 a = Matrix4::translation({1.0f, -5.0f, 3.5f})*Matrix4::rotation(deg(90.0f), Vector3::zAxis()); + Vector3 v(1.0f, -2.0f, 5.5f); + + CORRADE_COMPARE(a.transformVector(v), Vector3(2.0f, 1.0f, 5.5f)); + CORRADE_COMPARE(a.transformPoint(v), Vector3(3.0f, -4.0f, 9.0f)); +} + void Matrix4Test::debug() { Matrix4 m({3.0f, 5.0f, 8.0f, 4.0f}, {4.0f, 4.0f, 7.0f, 3.0f}, diff --git a/src/Math/Test/QuaternionTest.cpp b/src/Math/Test/QuaternionTest.cpp index f17a6e93b..1a39fc565 100644 --- a/src/Math/Test/QuaternionTest.cpp +++ b/src/Math/Test/QuaternionTest.cpp @@ -299,7 +299,7 @@ void QuaternionTest::rotateVector() { Vector3 v(5.0f, -3.6f, 0.7f); Vector3 rotated = a.rotateVector(v); - CORRADE_COMPARE(rotated, (m*Vector4(v, 0.0f)).xyz()); + CORRADE_COMPARE(rotated, m.transformVector(v)); CORRADE_COMPARE(rotated, Vector3(5.0f, -3.58733f, -0.762279f)); } @@ -315,7 +315,7 @@ void QuaternionTest::rotateVectorNormalized() { CORRADE_COMPARE(o.str(), "Math::Quaternion::rotateVectorNormalized(): quaternion must be normalized\n"); Vector3 rotated = a.rotateVectorNormalized(v); - CORRADE_COMPARE(rotated, (m*Vector4(v, 0.0f)).xyz()); + CORRADE_COMPARE(rotated, m.transformVector(v)); CORRADE_COMPARE(rotated, a.rotateVector(v)); } diff --git a/src/Physics/AxisAlignedBox.cpp b/src/Physics/AxisAlignedBox.cpp index 786003a40..822315464 100644 --- a/src/Physics/AxisAlignedBox.cpp +++ b/src/Physics/AxisAlignedBox.cpp @@ -22,8 +22,8 @@ namespace Magnum { namespace Physics { template void AxisAlignedBox::applyTransformationMatrix(const typename DimensionTraits::MatrixType& matrix) { - _transformedMin = (matrix*typename DimensionTraits::PointType(_min)).vector(); - _transformedMax = (matrix*typename DimensionTraits::PointType(_max)).vector(); + _transformedMin = matrix.transformPoint(_min); + _transformedMax = matrix.transformPoint(_max); } template bool AxisAlignedBox::collides(const AbstractShape* other) const { diff --git a/src/Physics/Capsule.cpp b/src/Physics/Capsule.cpp index 784b016d9..fd3fc9d79 100644 --- a/src/Physics/Capsule.cpp +++ b/src/Physics/Capsule.cpp @@ -28,8 +28,8 @@ using namespace Magnum::Math::Geometry; namespace Magnum { namespace Physics { template void Capsule::applyTransformationMatrix(const typename DimensionTraits::MatrixType& matrix) { - _transformedA = (matrix*typename DimensionTraits::PointType(_a)).vector(); - _transformedB = (matrix*typename DimensionTraits::PointType(_b)).vector(); + _transformedA = matrix.transformPoint(_a); + _transformedB = matrix.transformPoint(_b); float scaling = (matrix.rotationScaling()*typename DimensionTraits::VectorType(1/Constants::sqrt3())).length(); _transformedRadius = scaling*_radius; } diff --git a/src/Physics/Line.cpp b/src/Physics/Line.cpp index 49fbe566e..8f02bc887 100644 --- a/src/Physics/Line.cpp +++ b/src/Physics/Line.cpp @@ -21,8 +21,8 @@ namespace Magnum { namespace Physics { template void Line::applyTransformationMatrix(const typename DimensionTraits::MatrixType& matrix) { - _transformedA = (matrix*typename DimensionTraits::PointType(_a)).vector(); - _transformedB = (matrix*typename DimensionTraits::PointType(_b)).vector(); + _transformedA = matrix.transformPoint(_a); + _transformedB = matrix.transformPoint(_b); } /* Explicitly instantiate the templates */ diff --git a/src/Physics/Plane.cpp b/src/Physics/Plane.cpp index 77af71cc9..471f1a0ed 100644 --- a/src/Physics/Plane.cpp +++ b/src/Physics/Plane.cpp @@ -27,7 +27,7 @@ using namespace Magnum::Math::Geometry; namespace Magnum { namespace Physics { void Plane::applyTransformationMatrix(const Matrix4& matrix) { - _transformedPosition = (matrix*Magnum::Point3D(_position)).xyz(); + _transformedPosition = matrix.transformPoint(_position); _transformedNormal = matrix.rotation()*_normal; } diff --git a/src/Physics/Point.cpp b/src/Physics/Point.cpp index 53f697109..94d7d20af 100644 --- a/src/Physics/Point.cpp +++ b/src/Physics/Point.cpp @@ -21,7 +21,7 @@ namespace Magnum { namespace Physics { template void Point::applyTransformationMatrix(const typename DimensionTraits::MatrixType& matrix) { - _transformedPosition = (matrix*typename DimensionTraits::PointType(_position)).vector(); + _transformedPosition = matrix.transformPoint(_position); } template class Point<2>; diff --git a/src/Physics/Sphere.cpp b/src/Physics/Sphere.cpp index c0cf2aa33..a307844d0 100644 --- a/src/Physics/Sphere.cpp +++ b/src/Physics/Sphere.cpp @@ -40,7 +40,7 @@ namespace { } template void Sphere::applyTransformationMatrix(const typename DimensionTraits::MatrixType& matrix) { - _transformedPosition = (matrix*typename DimensionTraits::PointType(_position)).vector(); + _transformedPosition = matrix.transformPoint(_position); float scaling = (matrix.rotationScaling()*unitVector()).length(); _transformedRadius = scaling*_radius; }