Browse Source

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;
pull/7/head
Vladimír Vondruš 13 years ago
parent
commit
a49bb0d718
  1. 2
      src/Math/DualQuaternion.h
  2. 24
      src/Math/Matrix3.h
  3. 25
      src/Math/Matrix4.h
  4. 4
      src/Math/Quaternion.h
  5. 4
      src/Math/Test/DualQuaternionTest.cpp
  6. 14
      src/Math/Test/Matrix3Test.cpp
  7. 14
      src/Math/Test/Matrix4Test.cpp
  8. 4
      src/Math/Test/QuaternionTest.cpp
  9. 4
      src/Physics/AxisAlignedBox.cpp
  10. 4
      src/Physics/Capsule.cpp
  11. 4
      src/Physics/Line.cpp
  12. 2
      src/Physics/Plane.cpp
  13. 2
      src/Physics/Point.cpp
  14. 2
      src/Physics/Sphere.cpp

2
src/Math/DualQuaternion.h

@ -218,7 +218,7 @@ template<class T> class DualQuaternion: public Dual<Quaternion<T>> {
* 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<T> transformPointNormalized(const Vector3<T>& vector) const {
CORRADE_ASSERT(MathTypeTraits<Dual<T>>::equals(norm(), Dual<T>(1)),

24
src/Math/Matrix3.h

@ -204,6 +204,30 @@ template<class T> 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<T> transformVector(const Vector2<T>& vector) const {
return ((*this)*Vector3<T>(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<T> transformPoint(const Vector2<T>& vector) const {
return ((*this)*Vector3<T>(vector, T(1))).xy();
}
#ifndef DOXYGEN_GENERATING_OUTPUT
inline Point2D<T> operator*(const Point2D<T>& other) const {
return Matrix<3, T>::operator*(other);

25
src/Math/Matrix4.h

@ -348,6 +348,31 @@ template<class T> 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<T> transformVector(const Vector3<T>& vector) const {
return ((*this)*Vector4<T>(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<T> transformPoint(const Vector3<T>& vector) const {
return ((*this)*Vector4<T>(vector, T(1))).xyz();
}
#ifndef DOXYGEN_GENERATING_OUTPUT
inline Point3D<T> operator*(const Point3D<T>& other) const {
return Matrix<4, T>::operator*(other);

4
src/Math/Quaternion.h

@ -403,7 +403,7 @@ template<class T> class Quaternion {
* quaternions. @f[
* v' = qvq^{-1} = q [\boldsymbol v, 0] q^{-1}
* @f]
* @see DualQuaternion::transformPointNormalized()
* @see Matrix4::transformVector(), DualQuaternion::transformPointNormalized()
*/
inline Vector3<T> rotateVector(const Vector3<T>& vector) const {
return ((*this)*Quaternion<T>(vector)*inverted()).vector();
@ -416,7 +416,7 @@ template<class T> class Quaternion {
* normalized. @f[
* v' = qvq^{-1} = qvq^* = q [\boldsymbol v, 0] q^*
* @f]
* @see DualQuaternion::transformPointNormalized()
* @see Matrix4::transformVector(), DualQuaternion::transformPointNormalized()
*/
inline Vector3<T> rotateVectorNormalized(const Vector3<T>& vector) const {
CORRADE_ASSERT(MathTypeTraits<T>::equals(dot(), T(1)),

4
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));
}

14
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<float> 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},

14
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<float> 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},

4
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));
}

4
src/Physics/AxisAlignedBox.cpp

@ -22,8 +22,8 @@
namespace Magnum { namespace Physics {
template<std::uint8_t dimensions> void AxisAlignedBox<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) {
_transformedMin = (matrix*typename DimensionTraits<dimensions>::PointType(_min)).vector();
_transformedMax = (matrix*typename DimensionTraits<dimensions>::PointType(_max)).vector();
_transformedMin = matrix.transformPoint(_min);
_transformedMax = matrix.transformPoint(_max);
}
template<std::uint8_t dimensions> bool AxisAlignedBox<dimensions>::collides(const AbstractShape<dimensions>* other) const {

4
src/Physics/Capsule.cpp

@ -28,8 +28,8 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Physics {
template<std::uint8_t dimensions> void Capsule<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) {
_transformedA = (matrix*typename DimensionTraits<dimensions>::PointType(_a)).vector();
_transformedB = (matrix*typename DimensionTraits<dimensions>::PointType(_b)).vector();
_transformedA = matrix.transformPoint(_a);
_transformedB = matrix.transformPoint(_b);
float scaling = (matrix.rotationScaling()*typename DimensionTraits<dimensions>::VectorType(1/Constants::sqrt3())).length();
_transformedRadius = scaling*_radius;
}

4
src/Physics/Line.cpp

@ -21,8 +21,8 @@
namespace Magnum { namespace Physics {
template<std::uint8_t dimensions> void Line<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) {
_transformedA = (matrix*typename DimensionTraits<dimensions>::PointType(_a)).vector();
_transformedB = (matrix*typename DimensionTraits<dimensions>::PointType(_b)).vector();
_transformedA = matrix.transformPoint(_a);
_transformedB = matrix.transformPoint(_b);
}
/* Explicitly instantiate the templates */

2
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;
}

2
src/Physics/Point.cpp

@ -21,7 +21,7 @@
namespace Magnum { namespace Physics {
template<std::uint8_t dimensions> void Point<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) {
_transformedPosition = (matrix*typename DimensionTraits<dimensions>::PointType(_position)).vector();
_transformedPosition = matrix.transformPoint(_position);
}
template class Point<2>;

2
src/Physics/Sphere.cpp

@ -40,7 +40,7 @@ namespace {
}
template<std::uint8_t dimensions> void Sphere<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) {
_transformedPosition = (matrix*typename DimensionTraits<dimensions>::PointType(_position)).vector();
_transformedPosition = matrix.transformPoint(_position);
float scaling = (matrix.rotationScaling()*unitVector<dimensions>()).length();
_transformedRadius = scaling*_radius;
}

Loading…
Cancel
Save