#ifndef Magnum_Math_RectangularMatrix_h #define Magnum_Math_RectangularMatrix_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::RectangularMatrix */ #include #include #include #include #include "MathTypeTraits.h" namespace Magnum { namespace Math { #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { template struct Sequence {}; /* E.g. GenerateSequence<3>::Type is Sequence<0, 1, 2> */ template struct GenerateSequence: GenerateSequence {}; template struct GenerateSequence<0, sequence...> { typedef Sequence Type; }; } #endif template class Vector; /** @brief Rectangular matrix @tparam c Column count @tparam r Row count See also Matrix (square) and Vector. */ template class RectangularMatrix { static_assert(c != 0 && r != 0, "Matrix cannot have zero elements"); friend class Vector; public: typedef T Type; /**< @brief Data type */ const static size_t cols = c; /**< @brief %Matrix column count */ const static size_t rows = r; /**< @brief %Matrix row count */ /** * @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 RectangularMatrix& from(T* data) { return *reinterpret_cast*>(data); } /** @overload */ inline constexpr static const RectangularMatrix& 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 RectangularMatrix from(const Vector& first, const U&... next) { static_assert(sizeof...(next)+1 == cols, "Improper number of arguments passed to Matrix from Vector constructor"); return from(typename Implementation::GenerateSequence::Type(), first, next...); } /** @brief Zero-filled matrix constructor */ inline constexpr RectangularMatrix(): _data() {} /** * @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 RectangularMatrix(T first, U... next): _data{first, next...} { static_assert(sizeof...(next)+1 == cols*rows, "Improper number of arguments passed to Matrix constructor"); } #else template inline constexpr RectangularMatrix(T first, U... next); #endif /** @brief Copy constructor */ inline constexpr RectangularMatrix(const RectangularMatrix&) = default; /** @brief Assignment operator */ inline RectangularMatrix& operator=(const RectangularMatrix&) = 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 * * For accessing individual elements prefer to use operator(), as it * is guaranteed to not involve unnecessary conversions. */ inline Vector& operator[](size_t col) { return Vector::from(_data+col*rows); } /** @overload */ inline constexpr const Vector& operator[](size_t col) const { return Vector::from(_data+col*rows); } /** * @brief Element on given position * * Prefer this instead of using `[][]`. * @see operator[] */ inline T& operator()(size_t col, size_t row) { return _data[col*rows+row]; } /** @overload */ inline constexpr const T& operator()(size_t col, size_t row) const { return _data[col*rows+row]; } /** @brief Equality operator */ inline bool operator==(const RectangularMatrix& other) const { for(size_t i = 0; i != cols*rows; ++i) if(!MathTypeTraits::equals(_data[i], other._data[i])) return false; return true; } /** @brief Non-equality operator */ inline constexpr bool operator!=(const RectangularMatrix& other) const { return !operator==(other); } /** @brief Multiply matrix */ template RectangularMatrix operator*(const RectangularMatrix& other) const { RectangularMatrix out; for(size_t row = 0; row != rows; ++row) for(size_t col = 0; col != size; ++col) /** @todo swap */ for(size_t pos = 0; pos != cols; ++pos) out(col, row) += (*this)(pos, row)*other(col, pos); return out; } /** * @brief Multiply vector * * Internally the same as multiplying with one-column matrix, but * returns vector. */ Vector operator*(const Vector& other) const { return operator*(static_cast>(other)); } /** @brief Transposed matrix */ RectangularMatrix transposed() const { RectangularMatrix out; for(size_t col = 0; col != cols; ++col) for(size_t row = 0; row != rows; ++row) out(row, col) = (*this)(col, row); return out; } private: template inline constexpr static RectangularMatrix from(Implementation::Sequence s, const Vector& first, U... next) { return from(s, next..., first[sequence]...); } template inline constexpr static RectangularMatrix from(Implementation::Sequence, T first, U... next) { return RectangularMatrix(first, next...); } T _data[rows*cols]; }; /** @debugoperator{Magnum::Math::RectangularMatrix} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::RectangularMatrix& value) { debug << "Matrix("; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); for(size_t row = 0; row != rows; ++row) { if(row != 0) debug << ",\n "; for(size_t col = 0; col != cols; ++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_RECTANGULARMATRIX_SUBCLASS_IMPLEMENTATION(cols, rows, ...) \ inline constexpr static __VA_ARGS__& from(T* data) { \ return *reinterpret_cast<__VA_ARGS__*>(data); \ } \ inline constexpr static const __VA_ARGS__& from(const T* data) { \ return *reinterpret_cast(data); \ } \ template inline constexpr static __VA_ARGS__ from(const Vector& first, const U&... next) { \ return RectangularMatrix::from(first, next...); \ } \ \ inline __VA_ARGS__& operator=(const RectangularMatrix& other) { \ RectangularMatrix::operator=(other); \ return *this; \ } #endif }} namespace Corrade { namespace Utility { /** @configurationvalue{Magnum::Math::RectangularMatrix} */ template struct ConfigurationValue> { /** @brief Writes elements separated with spaces */ static std::string toString(const Magnum::Math::RectangularMatrix& value, int flags = 0) { std::string output; for(size_t row = 0; row != rows; ++row) { for(size_t col = 0; col != cols; ++col) { if(!output.empty()) output += ' '; output += ConfigurationValue::toString(value(col, row), flags); } } return output; } /** @brief Reads elements separated with whitespace */ static Magnum::Math::RectangularMatrix fromString(const std::string& stringValue, int flags = 0) { Magnum::Math::RectangularMatrix result; std::istringstream in(stringValue); for(size_t row = 0; row != rows; ++row) { for(size_t col = 0; col != cols; ++col) { std::string num; in >> num; result(col, row) = ConfigurationValue::fromString(num, flags); } } return result; } }; }} /* Include also Vector, so the definition is complete */ #include "Vector.h" #endif