#ifndef Magnum_Math_Matrix_h #define Magnum_Math_Matrix_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš This file is part of Magnum. Magnum is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 only, as published by the Free Software Foundation. Magnum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License version 3 for more details. */ /** @file * @brief Class Magnum::Math::Matrix */ #include "Vector.h" namespace Magnum { namespace Math { #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { template class MatrixDeterminant; } #endif /** * @brief %Matrix * * @todo @c PERFORMANCE - loop unrolling for Matrix<3, T> and Matrix<4, T> * @todo first col, then row (cache adjacency) */ template class Matrix { static_assert(size != 0, "Matrix cannot have zero elements"); friend class Matrix; /* for ij() */ public: const static size_t Size = size; /**< @brief %Matrix size */ typedef T Type; /**< @brief %Matrix data type */ /** * @brief %Matrix from array * @return Reference to the data as if it was Matrix, thus doesn't * perform any copying. * * @attention Use with caution, the function doesn't check whether the * array is long enough. */ inline constexpr static Matrix& from(T* data) { return *reinterpret_cast*>(data); } /** @overload */ inline constexpr static const Matrix& from(const T* data) { return *reinterpret_cast*>(data); } /** * @brief %Matrix from column vectors * @param first First column vector * @param next Next column vectors */ template inline constexpr static Matrix from(const Vector& first, const U&... next) { static_assert(sizeof...(next)+1 == size, "Improper number of arguments passed to Matrix from Vector constructor"); return from(typename Implementation::GenerateSequence::Type(), first, next...); } /** @brief Pass to constructor to create zero-filled matrix */ enum ZeroType { Zero }; /** * @brief Zero-filled matrix constructor * * Use this constructor by calling `Matrix m(Matrix::Zero);`. */ inline constexpr explicit Matrix(ZeroType): _data() {} /** @brief Pass to constructor to create identity matrix */ enum IdentityType { Identity }; /** * @brief Default constructor * * You can also explicitly call this constructor with * `Matrix m(Matrix::Identity);`. Optional parameter @p value allows * you to specify value on diagonal. */ inline explicit Matrix(IdentityType = Identity, T value = T(1)): _data() { for(size_t i = 0; i != size; ++i) _data[size*i+i] = value; } /** * @brief Initializer-list constructor * @param first First value * @param next Next values * * Note that the values are in column-major order. * @todoc Remove workaround when Doxygen supports uniform initialization */ #ifndef DOXYGEN_GENERATING_OUTPUT template inline constexpr Matrix(T first, U... next): _data{first, next...} { static_assert(sizeof...(next)+1 == size*size, "Improper number of arguments passed to Matrix constructor"); } #else template inline constexpr Matrix(T first, U... next); #endif /** @brief Copy constructor */ inline constexpr Matrix(const Matrix&) = default; /** @brief Assignment operator */ inline Matrix& operator=(const Matrix&) = default; /** * @brief Raw data * @return One-dimensional array of `size*size` length in column-major * order. */ inline T* data() { return _data; } inline constexpr const T* data() const { return _data; } /**< @overload */ /** @brief %Matrix column */ inline Vector& operator[](size_t col) { return Vector::from(_data+col*size); } /** @overload */ inline constexpr const Vector& operator[](size_t col) const { return Vector::from(_data+col*size); } /** @brief Equality operator */ inline bool operator==(const Matrix& other) const { for(size_t i = 0; i != size*size; ++i) if(!MathTypeTraits::equals(_data[i], other._data[i])) return false; return true; } /** @brief Non-equality operator */ inline constexpr bool operator!=(const Matrix& other) const { return !operator==(other); } /** @brief Multiply matrix operator */ Matrix operator*(const Matrix& other) const { Matrix out(Zero); for(size_t row = 0; row != size; ++row) for(size_t col = 0; col != size; ++col) for(size_t pos = 0; pos != size; ++pos) out(col, row) += (*this)(pos, row)*other(col, pos); return out; } /** @brief Multiply and assign matrix operator */ inline Matrix& operator*=(const Matrix& other) { return (*this = *this*other); } /** @brief Multiply vector operator */ Vector operator*(const Vector& other) const { Vector out; for(size_t row = 0; row != size; ++row) for(size_t pos = 0; pos != size; ++pos) out[row] += (*this)(pos, row)*other[pos]; return out; } /** @brief Transposed matrix */ Matrix transposed() const { Matrix out(Zero); for(size_t row = 0; row != size; ++row) for(size_t col = 0; col != size; ++col) out(row, col) = (*this)(col, row); return out; } /** @brief %Matrix without given column and row */ Matrix ij(size_t skipCol, size_t skipRow) const { Matrix out(Matrix::Zero); for(size_t row = 0; row != size-1; ++row) for(size_t col = 0; col != size-1; ++col) out(col, row) = (*this)(col + (col >= skipCol), row + (row >= skipRow)); return out; } /** * @brief Determinant * * Computed recursively using Laplace's formula: * @f[ * \det(A) = \sum_{j=1}^n (-1)^{i+j} a_{i,j} \det(A^{i,j}) * @f] * @f$ A^{i, j} @f$ is matrix without i-th row and j-th column, see * ij(). The formula is expanded down to 2x2 matrix, where the * determinant is computed directly: * @f[ * \det(A) = a_{0, 0} a_{1, 1} - a_{1, 0} a_{0, 1} * @f] */ inline T determinant() const { return Implementation::MatrixDeterminant()(*this); } /** * @brief Inverted matrix * * Computed using Cramer's rule: * @f[ * A^{-1} = \frac{1}{\det(A)} Adj(A) * @f] */ Matrix inverted() const { Matrix out(Zero); T _determinant = determinant(); for(size_t row = 0; row != size; ++row) for(size_t col = 0; col != size; ++col) out(col, row) = (((row+col) & 1) ? -1 : 1)*ij(row, col).determinant()/_determinant; return out; } private: template inline constexpr static Matrix from(Implementation::Sequence s, const Vector& first, U... next) { return from(s, next..., first[sequence]...); } template inline constexpr static Matrix from(Implementation::Sequence, T first, U... next) { return Matrix(first, next...); } /* Used internally instead of [][], because GCC does some heavy optimalization in release mode which breaks it */ inline T& operator()(size_t col, size_t row) { return _data[col*size+row]; } inline constexpr const T& operator()(size_t col, size_t row) const { return _data[col*size+row]; } T _data[size*size]; }; /** @debugoperator{Matrix} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Matrix& value) { debug << "Matrix("; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); for(size_t row = 0; row != size; ++row) { if(row != 0) debug << ",\n "; for(size_t col = 0; col != size; ++col) { if(col != 0) debug << ", "; debug << typename MathTypeTraits::NumericType(value[col][row]); } } debug << ')'; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); return debug; } #ifndef DOXYGEN_GENERATING_OUTPUT #define MAGNUM_MATRIX_SUBCLASS_IMPLEMENTATION(Type, VectorType, size) \ inline constexpr static Type& from(T* data) { \ return *reinterpret_cast*>(data); \ } \ inline constexpr static const Type& from(const T* data) { \ return *reinterpret_cast*>(data); \ } \ template inline constexpr static Type from(const Vector& first, const U&... next) { \ return Matrix::from(first, next...); \ } \ \ inline Type& operator=(const Type& other) { \ Matrix::operator=(other); \ return *this; \ } \ \ inline VectorType& operator[](size_t col) { \ return VectorType::from(Matrix::data()+col*size); \ } \ inline constexpr const VectorType& operator[](size_t col) const { \ return VectorType::from(Matrix::data()+col*size); \ } \ \ inline Type operator*(const Matrix& other) const { \ return Matrix::operator*(other); \ } \ inline Type& operator*=(const Matrix& other) { \ Matrix::operator*=(other); \ return *this; \ } \ inline VectorType operator*(const Vector& other) const { \ return Matrix::operator*(other); \ } \ \ inline Type transposed() const { return Matrix::transposed(); } \ inline Type inverted() const { return Matrix::inverted(); } namespace Implementation { template class MatrixDeterminant { public: /** @brief Functor */ T operator()(const Matrix& m) { T out(0); for(size_t col = 0; col != size; ++col) out += ((col & 1) ? -1 : 1)*m[col][0]*m.ij(col, 0).determinant(); return out; } }; template class MatrixDeterminant<2, T> { public: /** @brief Functor */ inline constexpr T operator()(const Matrix<2, T>& m) { return m[0][0]*m[1][1] - m[1][0]*m[0][1]; } }; template class MatrixDeterminant<1, T> { public: /** @brief Functor */ inline constexpr T operator()(const Matrix<1, T>& m) { return m[0][0]; } }; } #endif }} #endif