diff --git a/doc/namespaces.dox b/doc/namespaces.dox index 63587bca1..faf2cb178 100644 --- a/doc/namespaces.dox +++ b/doc/namespaces.dox @@ -28,6 +28,15 @@ Base classes for creating OpenGL contexts with various toolkits. Template classes for matrix and vector calculations. */ +/** @dir Math/Algorithms + * @brief Namespace Magnum::Math::Algorithms + */ +/** @namespace Magnum::Math::Algorithms +@brief %Algorithms + +Various matrix and vector algorithms. +*/ + /** @dir Math/Geometry * @brief Namespace Magnum::Math::Geometry */ diff --git a/src/Buffer.h b/src/Buffer.h index c76260f3e..54af31cf4 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -237,6 +237,19 @@ class Buffer { setData(_defaultTarget, size, data, usage); } + /** + * @brief Set buffer data + * @param data Fixed-size array with data + * @param usage %Buffer usage + * + * Sets buffer data with default target. More convenient for setting + * data from fixed-size arrays than + * setData(GLsizeiptr, const GLvoid*, Usage). + */ + template inline void setData(const T(&data)[size], Usage usage) { + setData(_defaultTarget, data, usage); + } + /** * @brief Set buffer data * @param data Vector with data @@ -260,6 +273,19 @@ class Buffer { glBufferData(static_cast(target), size, data, static_cast(usage)); } + /** + * @brief Set buffer data + * @param target %Target + * @param data Fixed-size array with data + * @param usage %Buffer usage + * + * More convenient for setting data from fixed-size arrays than + * setData(Target, GLsizeiptr, const GLvoid*, Usage). + */ + template inline void setData(Target target, const T(&data)[size], Usage usage) { + setData(target, size*sizeof(T), data, usage); + } + /** * @brief Set buffer data * @param target %Target @@ -282,6 +308,19 @@ class Buffer { setSubData(_defaultTarget, offset, size, data); } + /** + * @brief Set buffer subdata + * @param offset Offset + * @param data Fixed-size array with data + * + * Sets buffer subdata with default target. More convenient for + * setting data from fixed-size arrays than + * setSubData(GLintptr, GLsizeiptr, const GLvoid*). + */ + template inline void setSubData(GLintptr offset, const T(&data)[size]) { + setSubData(_defaultTarget, offset, data); + } + /** * @brief Set buffer subdata * @param offset Offset @@ -305,6 +344,19 @@ class Buffer { glBufferSubData(static_cast(target), offset, size, data); } + /** + * @brief Set buffer subdata + * @param target %Target + * @param offset Offset + * @param data Fixed-size array with data + * + * More convenient for setting data from fixed-size arrays than + * setSubData(Target, GLintptr, GLsizeiptr, const GLvoid*). + */ + template inline void setSubData(Target target, GLintptr offset, const T(&data)[size]) { + setSubData(target, offset, size*sizeof(T), data); + } + /** * @brief Set buffer subdata * @param target %Target diff --git a/src/Color.h b/src/Color.h index c95e010a3..304c5408e 100644 --- a/src/Color.h +++ b/src/Color.h @@ -213,8 +213,8 @@ template class Color3: public Math::Vector3 { */ inline constexpr explicit Color3(T rgb): Math::Vector3(rgb) {} - /** @copydoc Math::Vector::Vector(const Vector&) */ - inline constexpr Color3(const Math::Vector<3, T>& other): Math::Vector3(other) {} + /** @brief Copy constructor */ + inline constexpr Color3(const Math::RectangularMatrix<1, 3, T>& other): Math::Vector3(other) {} /** * @brief Constructor @@ -276,8 +276,13 @@ template class Color3: public Math::Vector3 { inline constexpr FloatingPointType value() const { return Implementation::value(*this); } + + MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Color3, 3) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(1, 3, Color3) }; +MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Color3, 3) + /** @brief Four-component (RGBA) color @@ -335,8 +340,8 @@ template class Color4: public Math::Vector4 { */ inline constexpr explicit Color4(T rgb, T alpha = Implementation::defaultAlpha()): Math::Vector4(rgb, rgb, rgb, alpha) {} - /** @copydoc Math::Vector::Vector(const Vector&) */ - inline constexpr Color4(const Math::Vector<4, T>& other): Math::Vector4(other) {} + /** @brief Copy constructor */ + inline constexpr Color4(const Math::RectangularMatrix<1, 4, T>& other): Math::Vector4(other) {} /** * @brief Constructor @@ -394,8 +399,13 @@ template class Color4: public Math::Vector4 { inline constexpr FloatingPointType value() const { return Implementation::value(rgb()); } + + MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Color4, 4) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(1, 4, Color4) }; +MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Color4, 4) + /** @debugoperator{Magnum::Color3} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Color3& value) { return debug << static_cast&>(value); diff --git a/src/Image.h b/src/Image.h index 64557ea23..fd83832a7 100644 --- a/src/Image.h +++ b/src/Image.h @@ -76,7 +76,8 @@ template class Image: public AbstractImage { inline constexpr const Math::Vector& dimensions() const { return _dimensions; } /** @brief Pointer to raw data */ - inline constexpr const void* data() const { return _data; } + inline void* data() { return _data; } + inline constexpr const void* data() const { return _data; } /**< @overload */ /** * @brief Set image data diff --git a/src/ImageWrapper.h b/src/ImageWrapper.h index b4dc0e4e6..3a4ca8ce3 100644 --- a/src/ImageWrapper.h +++ b/src/ImageWrapper.h @@ -81,7 +81,8 @@ template class ImageWrapper: public AbstractImage { inline constexpr const Math::Vector& dimensions() const { return _dimensions; } /** @brief Pointer to raw data */ - inline constexpr const void* data() const { return _data; } + inline void* data() { return _data; } + inline constexpr const void* data() const { return _data; } /**< @overload */ /** * @brief Set image data diff --git a/src/Math/Algorithms/CMakeLists.txt b/src/Math/Algorithms/CMakeLists.txt new file mode 100644 index 000000000..cb5121b20 --- /dev/null +++ b/src/Math/Algorithms/CMakeLists.txt @@ -0,0 +1,8 @@ +set(MagnumMathAlgorithms_HEADERS + GaussJordan.h) +install(FILES ${MagnumMathAlgorithms_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Math/Algorithms) + +if(BUILD_TESTS) + enable_testing() + add_subdirectory(Test) +endif() diff --git a/src/Math/Algorithms/GaussJordan.h b/src/Math/Algorithms/GaussJordan.h new file mode 100644 index 000000000..d98bf89c8 --- /dev/null +++ b/src/Math/Algorithms/GaussJordan.h @@ -0,0 +1,113 @@ +#ifndef Magnum_Math_Algorithms_GaussJordan_h +#define Magnum_Math_Algorithms_GaussJordan_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 GaussJordan + */ + +#include "Math/RectangularMatrix.h" + +namespace Magnum { namespace Math { namespace Algorithms { + +/** +@brief Gauss-Jordan elimination + +Based on ultra-compact Python code by Jarno Elonen, +http://elonen.iki.fi/code/misc-notes/python-gaussj/index.html. +*/ +class GaussJordan { + public: + /** + * @brief Eliminate transposed matrices in place + * @param a Transposed left side of augmented matrix + * @param t Transposed right side of augmented matrix + * @return True if @p a is regular, false if @p a is singular (and + * thus the system cannot be solved). + * + * As Gauss-Jordan elimination works on rows and matrices in OpenGL + * are column-major, it is more efficient to operate on transposed + * matrices and treat columns as rows. See also inPlace() which works + * with non-transposed matrices. + * + * The function eliminates matrix @p a and solves @p t in place. For + * efficiency reasons, only pure Gaussian elimination is done on @p a + * and the final backsubstitution is done only on @p t, as @p a would + * always end with identity matrix anyway. + */ + template static bool inPlaceTransposed(RectangularMatrix& a, RectangularMatrix& t); + + /** + * @brief Eliminate in place + * + * Transposes the matrices, calls inPlaceTransposed() on them and then + * transposes them back. + */ + template static bool inPlace(RectangularMatrix& a, RectangularMatrix& t) { + a = a.transposed(); + RectangularMatrix tTransposed = t.transposed(); + + bool ret = inPlaceTransposed(a, tTransposed); + + a = a.transposed(); + t = tTransposed.transposed(); + + return ret; + } +}; + +template bool GaussJordan::inPlaceTransposed(RectangularMatrix& a, RectangularMatrix& t) { + for(size_t row = 0; row != size; ++row) { + /* Find max pivot */ + size_t rowMax = row; + for(size_t row2 = row+1; row2 != size; ++row2) + if(std::abs(a(row2, row)) > std::abs(a(rowMax, row))) + rowMax = row2; + + /* Swap the rows */ + std::swap(a[row], a[rowMax]); + std::swap(t[row], t[rowMax]); + + /* Singular */ + if(MathTypeTraits::equals(a(row, row), 0)) + return false; + + /* Eliminate column */ + for(size_t row2 = row+1; row2 != size; ++row2) { + T c = a(row2, row)/a(row, row); + + a[row2] -= a[row]*c; + t[row2] -= t[row]*c; + } + } + + /* Backsubstitute */ + for(size_t row = size; row != 0; --row) { + T c = T(1)/a(row-1, row-1); + + for(size_t row2 = 0; row2 != row-1; ++row2) + t[row2] -= t[row-1]*a(row2, row-1)*c; + + /* Normalize the row */ + t[row-1] *= c; + } + + return true; +} + +}}} + +#endif diff --git a/src/Math/Algorithms/Test/CMakeLists.txt b/src/Math/Algorithms/Test/CMakeLists.txt new file mode 100644 index 000000000..8ebf44bc1 --- /dev/null +++ b/src/Math/Algorithms/Test/CMakeLists.txt @@ -0,0 +1 @@ +corrade_add_test2(MathAlgorithmsGaussJordanTest GaussJordanTest.cpp) diff --git a/src/Math/Algorithms/Test/GaussJordanTest.cpp b/src/Math/Algorithms/Test/GaussJordanTest.cpp new file mode 100644 index 000000000..51e36d89d --- /dev/null +++ b/src/Math/Algorithms/Test/GaussJordanTest.cpp @@ -0,0 +1,60 @@ +/* + 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. +*/ + +#include "GaussJordanTest.h" + +#include "Math/Algorithms/GaussJordan.h" + +CORRADE_TEST_MAIN(Magnum::Math::Algorithms::Test::GaussJordanTest) + +namespace Magnum { namespace Math { namespace Algorithms { namespace Test { + +typedef RectangularMatrix<4, 4, float> Matrix4; + +GaussJordanTest::GaussJordanTest() { + addTests(&GaussJordanTest::singular, + &GaussJordanTest::invert); +} + +void GaussJordanTest::singular() { + Matrix4 a(1.0f, 2.0f, 3.0f, 4.0f, + 2.0f, 3.0f, -7.0f, 11.0f, + 2.0f, 4.0f, 6.0f, 8.0f, + 1.0f, 2.0f, 7.0f, 40.0f); + RectangularMatrix<4, 1, float> t; + + CORRADE_VERIFY(!GaussJordan::inPlaceTransposed(a, t)); +} + +void GaussJordanTest::invert() { + Matrix4 a(3.0f, 5.0f, 8.0f, 4.0f, + 4.0f, 4.0f, 7.0f, 3.0f, + 7.0f, -1.0f, 8.0f, 0.0f, + 9.0f, 4.0f, 5.0f, 9.0f); + + Matrix4 expectedInverse(-60/103.0f, 71/103.0f, -4/103.0f, 3/103.0f, + -66/103.0f, 109/103.0f, -25/103.0f, -7/103.0f, + 177/412.0f, -97/206.0f, 53/412.0f, -7/206.0f, + 259/412.0f, -185/206.0f, 31/412.0f, 27/206.0f); + + Matrix4 a2(a); + Matrix4 inverse; + CORRADE_VERIFY(GaussJordan::inPlace(a2, inverse)); + + CORRADE_COMPARE(inverse, expectedInverse); + CORRADE_COMPARE(a*inverse, Matrix4()); +} + +}}}} diff --git a/src/Math/Algorithms/Test/GaussJordanTest.h b/src/Math/Algorithms/Test/GaussJordanTest.h new file mode 100644 index 000000000..0c25d27e0 --- /dev/null +++ b/src/Math/Algorithms/Test/GaussJordanTest.h @@ -0,0 +1,32 @@ +#ifndef Magnum_Math_Algorithms_Test_GaussJordanTest_h +#define Magnum_Math_Algorithms_Test_GaussJordanTest_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. +*/ + +#include + +namespace Magnum { namespace Math { namespace Algorithms { namespace Test { + +class GaussJordanTest: public Corrade::TestSuite::Tester { + public: + GaussJordanTest(); + + void singular(); + void invert(); +}; + +}}}} + +#endif diff --git a/src/Math/CMakeLists.txt b/src/Math/CMakeLists.txt index a0cc17b69..cf24e5d0b 100644 --- a/src/Math/CMakeLists.txt +++ b/src/Math/CMakeLists.txt @@ -6,12 +6,14 @@ set(MagnumMath_HEADERS Matrix.h Matrix3.h Matrix4.h + RectangularMatrix.h Vector.h Vector2.h Vector3.h Vector4.h) install(FILES ${MagnumMath_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Math) +add_subdirectory(Algorithms) add_subdirectory(Geometry) if(BUILD_TESTS) diff --git a/src/Math/Math.h b/src/Math/Math.h index a90e680c7..f5f718ae4 100644 --- a/src/Math/Math.h +++ b/src/Math/Math.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "magnumCompatibility.h" #include "magnumVisibility.h" @@ -130,6 +131,11 @@ template inline constexpr typename std::ena FloatingPoint(value*std::numeric_limits::min())); } +/** @brief Clamp value */ +template inline T clamp(T value, T min, T max) { + return std::min(std::max(value, min), max); +} + /** @brief Angle in degrees diff --git a/src/Math/MathTypeTraits.h b/src/Math/MathTypeTraits.h index c42dbb84f..1c2385c7d 100644 --- a/src/Math/MathTypeTraits.h +++ b/src/Math/MathTypeTraits.h @@ -19,6 +19,7 @@ * @brief Class Magnum::Math::MathTypeTraits */ +#include #include #include "magnumCompatibility.h" diff --git a/src/Math/Matrix.h b/src/Math/Matrix.h index 263185396..2273ce3b1 100644 --- a/src/Math/Matrix.h +++ b/src/Math/Matrix.h @@ -19,7 +19,7 @@ * @brief Class Magnum::Math::Matrix */ -#include "Vector.h" +#include "RectangularMatrix.h" namespace Magnum { namespace Math { @@ -30,46 +30,16 @@ namespace Implementation { #endif /** -@brief %Matrix +@brief Square matrix +@tparam s %Matrix size @configurationvalueref{Magnum::Math::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() */ - +template class Matrix: public RectangularMatrix { 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...); - } + const static size_t size = s; /**< @brief %Matrix size */ /** @brief Pass to constructor to create zero-filled matrix */ enum ZeroType { Zero }; @@ -79,7 +49,7 @@ template class Matrix { * * Use this constructor by calling `Matrix m(Matrix::Zero);`. */ - inline constexpr explicit Matrix(ZeroType): _data() {} + inline constexpr explicit Matrix(ZeroType) {} /** @brief Pass to constructor to create identity matrix */ enum IdentityType { Identity }; @@ -91,117 +61,38 @@ template class Matrix { * `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() { + inline explicit Matrix(IdentityType = Identity, T value = T(1)) { for(size_t i = 0; i != size; ++i) - _data[size*i+i] = value; + (*this)(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 - */ + /** @copydoc RectangularMatrix::RectangularMatrix(T, U...) */ #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"); - } + template inline constexpr Matrix(T first, U... next): RectangularMatrix(first, next...) {} #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; + inline constexpr Matrix(const RectangularMatrix& other): RectangularMatrix(other) {} - /** - * @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*size); - } - /** @overload */ - inline constexpr const Vector& operator[](size_t col) const { - return Vector::from(_data+col*size); + /** @brief Multiply and assign matrix operator */ + inline Matrix& operator*=(const RectangularMatrix& other) { + return (*this = *this*other); } /** - * @brief Element on given position + * @brief Trace of the matrix * - * Prefer this instead of using `[][]`. - * @see operator[] + * @f[ + * tr(A) = \sum_{i=1}^n a_{i,i} + * @f] */ - inline T& operator()(size_t col, size_t row) { - return _data[col*size+row]; - } - /** @overload */ - inline constexpr const T& operator()(size_t col, size_t row) const { - return _data[col*size+row]; - } - - /** @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); + T trace() const { + T out(0); - for(size_t row = 0; row != size; ++row) - for(size_t col = 0; col != size; ++col) - out(row, col) = (*this)(col, row); + for(size_t i = 0; i != size; ++i) + out += (*this)(i, i); return out; } @@ -254,32 +145,34 @@ template class Matrix { return out; } - private: - template inline constexpr static Matrix from(Implementation::Sequence s, const Vector& first, U... next) { - return from(s, next..., first[sequence]...); + #ifndef DOXYGEN_GENERATING_OUTPUT + /* Reimplementation of functions to return correct type */ + inline Matrix operator*(const Matrix& other) const { + return RectangularMatrix::operator*(other); } - - template inline constexpr static Matrix from(Implementation::Sequence, T first, U... next) { - return Matrix(first, next...); + template inline RectangularMatrix operator*(const RectangularMatrix& other) const { + return RectangularMatrix::operator*(other); } - - T _data[size*size]; + inline Vector operator*(const Vector& other) const { + return RectangularMatrix::operator*(other); + } + MAGNUM_RECTANGULARMATRIX_SUBCLASS_IMPLEMENTATION(size, size, Matrix) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(size, size, Matrix) + #endif }; +#ifndef DOXYGEN_GENERATING_OUTPUT +template inline typename std::enable_if::value, Matrix>::type operator*(U number, const Matrix& matrix) { + return number*RectangularMatrix(matrix); +} +template inline typename std::enable_if::value, Matrix>::type operator/(U number, const Matrix& matrix) { + return number/RectangularMatrix(matrix); +} +#endif + /** @debugoperator{Magnum::Math::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; + return debug << static_cast&>(value); } #ifndef DOXYGEN_GENERATING_OUTPUT @@ -313,6 +206,9 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili Matrix::operator*=(other); \ return *this; \ } \ + template inline RectangularMatrix operator*(const RectangularMatrix& other) const { \ + return Matrix::operator*(other); \ + } \ inline VectorType operator*(const Vector& other) const { \ return Matrix::operator*(other); \ } \ @@ -320,6 +216,14 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili inline Type transposed() const { return Matrix::transposed(); } \ inline Type inverted() const { return Matrix::inverted(); } +#define MAGNUM_MATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(Type, size) \ + template inline typename std::enable_if::value, Type>::type operator*(U number, const Type& matrix) { \ + return number*Matrix(matrix); \ + } \ + template inline typename std::enable_if::value, Type>::type operator/(U number, const Type& matrix) { \ + return number/Matrix(matrix); \ + } + namespace Implementation { template class MatrixDeterminant { @@ -357,40 +261,8 @@ template class MatrixDeterminant<1, T> { }} namespace Corrade { namespace Utility { - -/** @configurationvalue{Magnum::Math::Matrix} */ -template struct ConfigurationValue> { - /** @brief Writes elements separated with spaces */ - static std::string toString(const Magnum::Math::Matrix& value, int flags = 0) { - std::string output; - - for(size_t row = 0; row != size; ++row) { - for(size_t col = 0; col != size; ++col) { - if(!output.empty()) output += ' '; - output += ConfigurationValue::toString(value(col, row), flags); - } - } - - return output; - } - - /** @brief Reads elements separated with whitespace */ - static Magnum::Math::Matrix fromString(const std::string& stringValue, int flags = 0) { - Magnum::Math::Matrix result(Magnum::Math::Matrix::Zero); - std::istringstream in(stringValue); - - for(size_t row = 0; row != size; ++row) { - for(size_t col = 0; col != size; ++col) { - std::string num; - in >> num; - result(col, row) = ConfigurationValue::fromString(num, flags); - } - } - - return result; - } -}; - + /** @configurationvalue{Magnum::Math::Matrix} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; }} #endif diff --git a/src/Math/Matrix3.h b/src/Math/Matrix3.h index 1cf5640b8..e537de58a 100644 --- a/src/Math/Matrix3.h +++ b/src/Math/Matrix3.h @@ -92,10 +92,11 @@ template class Matrix3: public Matrix<3, T> { template inline constexpr Matrix3(T first, U... next) {} #endif - /** @copydoc Matrix::Matrix(const Matrix&) */ - inline constexpr Matrix3(const Matrix<3, T>& other): Matrix<3, T>(other) {} + /** @brief Copy constructor */ + inline constexpr Matrix3(const RectangularMatrix<3, 3, T>& other): Matrix<3, T>(other) {} MAGNUM_MATRIX_SUBCLASS_IMPLEMENTATION(Matrix3, Vector3, 3) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(3, 3, Matrix3) }; /** @debugoperator{Magnum::Math::Matrix3} */ diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index a1c558c72..027107400 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -122,8 +122,8 @@ template class Matrix4: public Matrix<4, T> { template inline constexpr Matrix4(T first, U... next) {} #endif - /** @copydoc Matrix::Matrix(const Matrix&) */ - inline constexpr Matrix4(const Matrix<4, T>& other): Matrix<4, T>(other) {} + /** @brief Copy constructor */ + inline constexpr Matrix4(const RectangularMatrix<4, 4, T>& other): Matrix<4, T>(other) {} /** @copydoc Matrix::ij() */ inline Matrix3 ij(size_t skipRow, size_t skipCol) const { return Matrix<4, T>::ij(skipRow, skipCol); } @@ -158,6 +158,7 @@ template class Matrix4: public Matrix<4, T> { } MAGNUM_MATRIX_SUBCLASS_IMPLEMENTATION(Matrix4, Vector4, 4) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(4, 4, Matrix4) }; /** @debugoperator{Magnum::Math::Matrix4} */ diff --git a/src/Math/RectangularMatrix.h b/src/Math/RectangularMatrix.h new file mode 100644 index 000000000..6c52764a8 --- /dev/null +++ b/src/Math/RectangularMatrix.h @@ -0,0 +1,467 @@ +#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 Add matrix + * + * @see operator+=() + */ + inline RectangularMatrix operator+(const RectangularMatrix& other) const { + return RectangularMatrix(*this)+=other; + } + + /** + * @brief Add and assign matrix + * + * More efficient than operator+(), because it does the computation + * in-place. + */ + RectangularMatrix& operator+=(const RectangularMatrix& other) { + for(size_t i = 0; i != cols*rows; ++i) + _data[i] += other._data[i]; + + return *this; + } + + /** @brief Negative matrix */ + RectangularMatrix operator-() const { + RectangularMatrix out; + + for(size_t i = 0; i != cols*rows; ++i) + out._data[i] = -_data[i]; + + return out; + } + + /** + * @brief Subtract matrix + * + * @see operator-=() + */ + inline RectangularMatrix operator-(const RectangularMatrix& other) const { + return RectangularMatrix(*this)-=other; + } + + /** + * @brief Subtract and assign matrix + * + * More efficient than operator-(), because it does the computation + * in-place. + */ + RectangularMatrix& operator-=(const RectangularMatrix& other) { + for(size_t i = 0; i != cols*rows; ++i) + _data[i] -= other._data[i]; + + return *this; + } + + /** + * @brief Multiply matrix with number + * + * @see operator*=(U), operator*(U, const RectangularMatrix&) + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template inline typename std::enable_if::value, RectangularMatrix>::type operator*(U number) const { + #else + template inline RectangularMatrix operator*(U number) const { + #endif + return RectangularMatrix(*this)*=number; + } + + /** + * @brief Multiply matrix with number and assign + * + * More efficient than operator*(U), because it does the computation + * in-place. + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template inline typename std::enable_if::value, RectangularMatrix&>::type operator*=(U number) { + #else + template RectangularMatrix& operator*=(U number) { + #endif + for(size_t i = 0; i != cols*rows; ++i) + _data[i] *= number; + + return *this; + } + + /** + * @brief Divide matrix with number + * + * @see operator/=(), operator/(U, const RectangularMatrix&) + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template inline typename std::enable_if::value, RectangularMatrix>::type operator/(U number) const { + #else + template inline RectangularMatrix operator/(U number) const { + #endif + return RectangularMatrix(*this)/=number; + } + + /** + * @brief Divide matrix with number and assign + * + * More efficient than operator/(), because it does the computation + * in-place. + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template inline typename std::enable_if::value, RectangularMatrix&>::type operator/=(U number) { + #else + template RectangularMatrix& operator/=(U number) { + #endif + for(size_t i = 0; i != cols*rows; ++i) + _data[i] /= number; + + return *this; + } + + /** @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]; +}; + +/** @relates RectangularMatrix +@brief Multiply number with matrix + +@see RectangularMatrix::operator*(U) const +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +template inline typename std::enable_if::value, RectangularMatrix>::type operator*(U number, const RectangularMatrix& matrix) { +#else +template inline RectangularMatrix operator*(U number, const RectangularMatrix& matrix) { +#endif + return matrix*number; +} + +/** @relates RectangularMatrix +@brief Divide matrix with number and invert + +Example: +@code +RectangularMatrix<2, 3, float> mat(1.0f, 2.0f, -4.0f, 8.0f, -1.0f, 0.5f); +RectangularMatrix<2, 3, float> another = 1.0f/mat; // {1.0f, 0.5f, -0.25f, 0.128f, -1.0f, 2.0f} +@endcode +@see RectangularMatrix::operator/() +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +template typename std::enable_if::value, RectangularMatrix>::type operator/(U number, const RectangularMatrix& matrix) { +#else +template RectangularMatrix operator/(U number, const RectangularMatrix& matrix) { +#endif + RectangularMatrix out; + + for(size_t i = 0; i != cols*rows; ++i) + out.data()[i] = number/matrix.data()[i]; + + return out; +} + +/** @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 Math::Vector& first, const U&... next) { \ + return Math::RectangularMatrix::from(first, next...); \ + } \ + \ + inline __VA_ARGS__& operator=(const Math::RectangularMatrix& other) { \ + Math::RectangularMatrix::operator=(other); \ + return *this; \ + } + +#define MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(cols, rows, ...) \ + inline __VA_ARGS__ operator-() const { \ + return Math::RectangularMatrix::operator-(); \ + } \ + inline __VA_ARGS__ operator+(const Math::RectangularMatrix& other) const { \ + return Math::RectangularMatrix::operator+(other); \ + } \ + inline __VA_ARGS__& operator+=(const Math::RectangularMatrix& other) { \ + Math::RectangularMatrix::operator+=(other); \ + return *this; \ + } \ + inline __VA_ARGS__ operator-(const Math::RectangularMatrix& other) const { \ + return Math::RectangularMatrix::operator-(other); \ + } \ + inline __VA_ARGS__& operator-=(const Math::RectangularMatrix& other) { \ + Math::RectangularMatrix::operator-=(other); \ + return *this; \ + } \ + template inline typename std::enable_if::value, __VA_ARGS__>::type operator*(U number) const { \ + return Math::RectangularMatrix::operator*(number); \ + } \ + template inline typename std::enable_if::value, __VA_ARGS__&>::type operator*=(U number) { \ + Math::RectangularMatrix::operator*=(number); \ + return *this; \ + } \ + template inline typename std::enable_if::value, __VA_ARGS__>::type operator/(U number) const { \ + return Math::RectangularMatrix::operator/(number); \ + } \ + template inline typename std::enable_if::value, __VA_ARGS__&>::type operator/=(U number) { \ + Math::RectangularMatrix::operator/=(number); \ + 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 diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index 05365c153..cd2050f55 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -1,5 +1,7 @@ corrade_add_test2(MathMathTypeTraitsTest MathTypeTraitsTest.cpp) +corrade_add_test2(MathRectangularMatrixTest RectangularMatrixTest.cpp) + corrade_add_test2(MathVectorTest VectorTest.cpp) set_target_properties(MathVectorTest PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) corrade_add_test2(MathVector2Test Vector2Test.cpp) diff --git a/src/Math/Test/MathTest.cpp b/src/Math/Test/MathTest.cpp index 9d76e9535..cfdc49243 100644 --- a/src/Math/Test/MathTest.cpp +++ b/src/Math/Test/MathTest.cpp @@ -28,6 +28,7 @@ MathTest::MathTest() { &MathTest::degrad, &MathTest::normalize, &MathTest::denormalize, + &MathTest::clamp, &MathTest::pow, &MathTest::log); } @@ -96,6 +97,12 @@ void MathTest::denormalize() { } } +void MathTest::clamp() { + CORRADE_COMPARE(Math::clamp(0.5f, -1.0f, 5.0f), 0.5f); + CORRADE_COMPARE(Math::clamp(-1.6f, -1.0f, 5.0f), -1.0f); + CORRADE_COMPARE(Math::clamp(9.5f, -1.0f, 5.0f), 5.0f); +} + void MathTest::pow() { CORRADE_COMPARE(Math::pow<10>(2ul), 1024ul); CORRADE_COMPARE(Math::pow<0>(3ul), 1ul); diff --git a/src/Math/Test/MathTest.h b/src/Math/Test/MathTest.h index 3bb32c5ea..568c0b148 100644 --- a/src/Math/Test/MathTest.h +++ b/src/Math/Test/MathTest.h @@ -27,6 +27,7 @@ class MathTest: public Corrade::TestSuite::Tester { void degrad(); void normalize(); void denormalize(); + void clamp(); void pow(); void log(); }; diff --git a/src/Math/Test/MatrixTest.cpp b/src/Math/Test/MatrixTest.cpp index d9e986c85..2d803ec16 100644 --- a/src/Math/Test/MatrixTest.cpp +++ b/src/Math/Test/MatrixTest.cpp @@ -32,15 +32,9 @@ typedef Vector<4, float> Vector4; MatrixTest::MatrixTest() { addTests(&MatrixTest::construct, - &MatrixTest::constructFromVectors, &MatrixTest::constructIdentity, &MatrixTest::constructZero, - &MatrixTest::data, - &MatrixTest::copy, - &MatrixTest::multiplyIdentity, - &MatrixTest::multiply, - &MatrixTest::multiplyVector, - &MatrixTest::transposed, + &MatrixTest::trace, &MatrixTest::ij, &MatrixTest::determinant, &MatrixTest::inverted, @@ -66,20 +60,6 @@ void MatrixTest::construct() { CORRADE_COMPARE(Matrix4::from(m), expected); } -void MatrixTest::constructFromVectors() { - Matrix4 actual = Matrix4::from(Vector4(1.0f, 2.0f, 3.0f, 4.0f), - Vector4(5.0f, 6.0f, 7.0f, 8.0f), - Vector4(9.0f, 10.0f, 11.0f, 12.0f), - Vector4(13.0f, 14.0f, 15.0f, 16.0f)); - - Matrix4 expected(1.0f, 2.0f, 3.0f, 4.0f, - 5.0f, 6.0f, 7.0f, 8.0f, - 9.0f, 10.0f, 11.0f, 12.0f, - 13.0f, 14.0f, 15.0f, 16.0f); - - CORRADE_COMPARE(actual, expected); -} - void MatrixTest::constructIdentity() { Matrix4 identity; Matrix4 identity2(Matrix4::Identity); @@ -117,120 +97,16 @@ void MatrixTest::constructZero() { CORRADE_COMPARE(zero, zeroExpected); } -void MatrixTest::data() { - Matrix4 m(Matrix4::Zero); - - Vector4 vector(4.0f, 5.0f, 6.0f, 7.0f); - - m[3] = vector; - m[2][1] = 1.0f; - - m(1, 2) = 1.5f; - - CORRADE_COMPARE(m(2, 1), 1.0f); - CORRADE_COMPARE(m[1][2], 1.5f); - CORRADE_COMPARE(m[3], vector); - - Matrix4 expected( - 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.5f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 4.0f, 5.0f, 6.0f, 7.0f - ); - - CORRADE_COMPARE(m, expected); -} - -void MatrixTest::copy() { - Matrix4 m1(Matrix4::Zero); - - m1(2, 3) = 1.0f; - - /* Copy */ - Matrix4 m2(m1); - Matrix4 m3; - m3(0, 0) = 1.0f; /* this line is here so it's not optimized to Matrix4 m3(m1) */ - m3 = m1; - - /* Change original */ - m1(3, 2) = 1.0f; - - /* Verify the copy is the same as original */ - Matrix4 original(Matrix4::Zero); - original(2, 3) = 1.0f; - - CORRADE_COMPARE(m2, original); - CORRADE_COMPARE(m3, original); -} - -void MatrixTest::multiplyIdentity() { - Matrix4 values( - 0.0f, 1.0f, 2.0f, 3.0f, - 4.0f, 5.0f, 6.0f, 7.0f, - 8.0f, 9.0f, 10.0f, 11.0f, - 12.0f, 13.0f, 14.0f, 15.0f - ); - - CORRADE_COMPARE(Matrix4()*values, values); - CORRADE_COMPARE(values*Matrix4(), values); -} - -void MatrixTest::multiply() { - Matrix<5, int> left( - -3, -3, -1, 3, -5, - -1, -3, -5, 2, 3, - -1, -4, 3, -1, -2, - -5, -5, -1, -4, -1, - 1, 3, -3, -4, -1 - ); - - Matrix<5, int> right( - 0, 5, 3, 4, 4, - 5, 5, 0, 0, -2, - 3, 2, -4, -3, 0, - -3, 0, -1, 2, -1, - 0, -1, -4, 4, 3 - ); - - Matrix<5, int> expected( - -24, -35, -32, -25, 1, - -22, -36, -24, 33, -8, - 8, 16, -22, 29, 2, - -1, 0, 1, -12, 16, - -12, 8, -20, -26, -2 - ); - - CORRADE_COMPARE((left *= right), expected); -} - -void MatrixTest::multiplyVector() { - Matrix<5, int> matrix( - -3, -3, -1, 3, -5, - -1, -3, -5, 2, 3, - -1, -4, 3, -1, -2, - -5, -5, -1, -4, -1, - 1, 3, -3, -4, -1 - ); - - CORRADE_COMPARE((matrix*Vector<5, int>(0, 5, 3, 4, 4)), (Vector<5, int>(-24, -35, -32, -25, 1))); -} - -void MatrixTest::transposed() { - Matrix4 original( - 0.0f, 1.0f, 2.0f, 3.0f, - 4.0f, 5.0f, 6.0f, 7.0f, - 8.0f, 9.0f, 10.0f, 11.0f, - 12.0f, 13.0f, 14.0f, 15.0f - ); - - Matrix4 transposed( - 0.0f, 4.0f, 8.0f, 12.0f, - 1.0f, 5.0f, 9.0f, 13.0f, - 2.0f, 6.0f, 10.0f, 14.0f, - 3.0f, 7.0f, 11.0f, 15.0f +void MatrixTest::trace() { + Matrix<5, int> m( + 1, 2, 3, 0, 0, + 2, 3, 2, 1, -2, + 1, 1, -20, 1, 0, + 2, 0, 0, 10, 2, + 3, 1, 0, 1, -2 ); - CORRADE_COMPARE(original.transposed(), transposed); + CORRADE_COMPARE(m.trace(), -8); } void MatrixTest::ij() { diff --git a/src/Math/Test/MatrixTest.h b/src/Math/Test/MatrixTest.h index bb4eab650..ba1cb02ee 100644 --- a/src/Math/Test/MatrixTest.h +++ b/src/Math/Test/MatrixTest.h @@ -24,15 +24,9 @@ class MatrixTest: public Corrade::TestSuite::Tester { MatrixTest(); void construct(); - void constructFromVectors(); void constructIdentity(); void constructZero(); - void data(); - void copy(); - void multiplyIdentity(); - void multiply(); - void multiplyVector(); - void transposed(); + void trace(); void ij(); void determinant(); void inverted(); diff --git a/src/Math/Test/RectangularMatrixTest.cpp b/src/Math/Test/RectangularMatrixTest.cpp new file mode 100644 index 000000000..da0762179 --- /dev/null +++ b/src/Math/Test/RectangularMatrixTest.cpp @@ -0,0 +1,237 @@ +/* + 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. +*/ + +#include "RectangularMatrixTest.h" + +#include + +#include "RectangularMatrix.h" + +CORRADE_TEST_MAIN(Magnum::Math::Test::RectangularMatrixTest) + +using namespace std; +using namespace Corrade::Utility; + +namespace Magnum { namespace Math { namespace Test { + +typedef RectangularMatrix<4, 3, float> Matrix4x3; +typedef RectangularMatrix<3, 4, float> Matrix3x4; +typedef RectangularMatrix<2, 2, float> Matrix2; +typedef Vector<4, float> Vector4; + +RectangularMatrixTest::RectangularMatrixTest() { + addTests(&RectangularMatrixTest::construct, + &RectangularMatrixTest::constructFromVectors, + &RectangularMatrixTest::constructZero, + &RectangularMatrixTest::data, + + &RectangularMatrixTest::negative, + &RectangularMatrixTest::addSubtract, + &RectangularMatrixTest::multiplyDivide, + &RectangularMatrixTest::multiply, + + &RectangularMatrixTest::transposed, + + &RectangularMatrixTest::debug, + &RectangularMatrixTest::configuration); +} + +void RectangularMatrixTest::construct() { + float m[] = { + 3.0f, 5.0f, 8.0f, 4.0f, + 4.0f, 4.0f, 7.0f, 3.0f, + 7.0f, -1.0f, 8.0f, 0.0f + }; + + Matrix3x4 expected( + 3.0f, 5.0f, 8.0f, 4.0f, + 4.0f, 4.0f, 7.0f, 3.0f, + 7.0f, -1.0f, 8.0f, 0.0f + ); + + CORRADE_COMPARE(Matrix3x4::from(m), expected); +} + +void RectangularMatrixTest::constructFromVectors() { + Matrix3x4 actual = Matrix3x4::from(Vector4(1.0f, 2.0f, 3.0f, 4.0f), + Vector4(5.0f, 6.0f, 7.0f, 8.0f), + Vector4(9.0f, 10.0f, 11.0f, 12.0f)); + + Matrix3x4 expected(1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f); + + CORRADE_COMPARE(actual, expected); +} + +void RectangularMatrixTest::constructZero() { + Matrix4x3 zero; + + Matrix4x3 zeroExpected( + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f + ); + + CORRADE_COMPARE(zero, zeroExpected); +} + +void RectangularMatrixTest::data() { + Matrix3x4 m; + + Vector4 vector(4.0f, 5.0f, 6.0f, 7.0f); + + m[2] = vector; + m[1][1] = 1.0f; + + m(0, 2) = 1.5f; + + CORRADE_COMPARE(m(1, 1), 1.0f); + CORRADE_COMPARE(m[0][2], 1.5f); + CORRADE_COMPARE(m[2], vector); + + Matrix3x4 expected( + 0.0f, 0.0f, 1.5f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 4.0f, 5.0f, 6.0f, 7.0f + ); + + CORRADE_COMPARE(m, expected); +} + +void RectangularMatrixTest::negative() { + CORRADE_COMPARE(-Matrix2(1.0f, -3.0f, 5.0f, -10.0f), Matrix2(-1.0f, 3.0f, -5.0f, 10.0f)); +} + +void RectangularMatrixTest::addSubtract() { + Matrix4x3 a(0.0f, 1.0f, 3.0f, + 4.0f, 5.0f, 7.0f, + 8.0f, 9.0f, 11.0f, + 12.0f, 13.0f, 15.0f); + Matrix4x3 b(-4.0f, 0.5f, 9.0f, + -9.0f, 11.0f, 0.25f, + 0.0f, -8.0f, 19.0f, + -3.0f, -5.0f, 2.0f); + Matrix4x3 e(-4.0f, 1.5f, 12.0f, + -5.0f, 16.0f, 7.25f, + 8.0f, 1.0f, 30.0f, + 9.0f, 8.0f, 17.0f); + + CORRADE_COMPARE(a + b, e); + CORRADE_COMPARE(e - b, a); +} + +void RectangularMatrixTest::multiplyDivide() { + Matrix2 vec(1.0f, 2.0f, 3.0f, 4.0f); + Matrix2 multiplied(-1.5f, -3.0f, -4.5f, -6.0f); + + CORRADE_COMPARE(vec*-1.5f, multiplied); + CORRADE_COMPARE(-1.5f*vec, multiplied); + CORRADE_COMPARE(multiplied/-1.5f, vec); + + Math::RectangularMatrix<1, 1, char> vecChar(32); + Math::RectangularMatrix<1, 1, char> multipliedChar(-48); + CORRADE_COMPARE(vecChar*-1.5f, multipliedChar); + CORRADE_COMPARE(multipliedChar/-1.5f, vecChar); + CORRADE_COMPARE(-1.5f*vecChar, multipliedChar); + + /* Divide vector with number and inverse */ + Matrix2 divisor(1.0f, 2.0f, -4.0f, 8.0f); + Matrix2 result(1.0f, 0.5f, -0.25f, 0.125f); + CORRADE_COMPARE(1.0f/divisor, result); + CORRADE_COMPARE(-1550.0f/multipliedChar, vecChar); +} + +void RectangularMatrixTest::multiply() { + RectangularMatrix<4, 6, int> left( + -5, 27, 10, 33, 0, -15, + 7, 56, 66, 1, 0, -24, + 4, 41, 4, 0, 1, -4, + 9, -100, 19, -49, 1, 9 + ); + + RectangularMatrix<5, 4, int> right( + 1, -7, 0, 158, + 2, 24, -3, 40, + 3, -15, -2, -50, + 4, 17, -1, -284, + 5, 30, 4, 18 + ); + + RectangularMatrix<5, 6, int> expected( + 1368, -16165, 2550, -7716, 158, 1575, + 506, -2725, 2352, -1870, 37, -234, + -578, 4159, -1918, 2534, -52, -127, + -2461, 29419, -4238, 14065, -285, -3020, + 363, 179, 2388, -687, 22, -649 + ); + + CORRADE_COMPARE(left*right, expected); +} + +void RectangularMatrixTest::transposed() { + Matrix4x3 original( + 0.0f, 1.0f, 3.0f, + 4.0f, 5.0f, 7.0f, + 8.0f, 9.0f, 11.0f, + 12.0f, 13.0f, 15.0f + ); + + Matrix3x4 transposed( + 0.0f, 4.0f, 8.0f, 12.0f, + 1.0f, 5.0f, 9.0f, 13.0f, + 3.0f, 7.0f, 11.0f, 15.0f + ); + + CORRADE_COMPARE(original.transposed(), transposed); +} + +void RectangularMatrixTest::debug() { + Matrix3x4 m( + 3.0f, 5.0f, 8.0f, 4.0f, + 4.0f, 4.0f, 7.0f, 3.0f, + 7.0f, -1.0f, 8.0f, 0.0f + ); + + ostringstream o; + Debug(&o) << m; + CORRADE_COMPARE(o.str(), "Matrix(3, 4, 7,\n" + " 5, 4, -1,\n" + " 8, 7, 8,\n" + " 4, 3, 0)\n"); + + o.str(""); + Debug(&o) << "a" << Matrix3x4() << "b" << RectangularMatrix<4, 3, char>(); + CORRADE_COMPARE(o.str(), "a Matrix(0, 0, 0,\n" + " 0, 0, 0,\n" + " 0, 0, 0,\n" + " 0, 0, 0) b Matrix(0, 0, 0, 0,\n" + " 0, 0, 0, 0,\n" + " 0, 0, 0, 0)\n"); +} + +void RectangularMatrixTest::configuration() { + Matrix3x4 m( + 3.0f, 5.0f, 8.0f, 4.0f, + 4.0f, 4.0f, 7.0f, 3.125f, + 7.0f, -1.0f, 8.0f, 9.55f + ); + string value("3 4 7 5 4 -1 8 7 8 4 3.125 9.55"); + CORRADE_COMPARE(ConfigurationValue::toString(m), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), m); +} + +}}} diff --git a/src/Math/Test/RectangularMatrixTest.h b/src/Math/Test/RectangularMatrixTest.h new file mode 100644 index 000000000..0eae0204a --- /dev/null +++ b/src/Math/Test/RectangularMatrixTest.h @@ -0,0 +1,44 @@ +#ifndef Magnum_Math_Test_RectangularMatrixTest_h +#define Magnum_Math_Test_RectangularMatrixTest_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. +*/ + +#include + +namespace Magnum { namespace Math { namespace Test { + +class RectangularMatrixTest: public Corrade::TestSuite::Tester { + public: + RectangularMatrixTest(); + + void construct(); + void constructFromVectors(); + void constructZero(); + void data(); + + void negative(); + void addSubtract(); + void multiplyDivide(); + void multiply(); + + void transposed(); + + void debug(); + void configuration(); +}; + +}}} + +#endif diff --git a/src/Math/Test/VectorTest.cpp b/src/Math/Test/VectorTest.cpp index cbafca1c9..4e18e3b69 100644 --- a/src/Math/Test/VectorTest.cpp +++ b/src/Math/Test/VectorTest.cpp @@ -34,12 +34,8 @@ typedef Vector<3, float> Vector3; VectorTest::VectorTest() { addTests(&VectorTest::construct, &VectorTest::constructFrom, - &VectorTest::data, - &VectorTest::copy, &VectorTest::dot, - &VectorTest::multiplyDivide, &VectorTest::multiplyDivideComponentWise, - &VectorTest::addSubtract, &VectorTest::dotSelf, &VectorTest::length, &VectorTest::normalized, @@ -48,7 +44,6 @@ VectorTest::VectorTest() { &VectorTest::min, &VectorTest::max, &VectorTest::angle, - &VectorTest::negative, &VectorTest::debug, &VectorTest::configuration); } @@ -69,61 +64,10 @@ void VectorTest::constructFrom() { CORRADE_COMPARE(Vector4::from(integral), floatingPointRounded); } -void VectorTest::data() { - Vector4 v; - v[2] = 1.5f; - - v[0] = 1.0f; - - CORRADE_COMPARE(v, Vector4(1.0f, 0.0f, 1.5f, 0.0f)); -} - -void VectorTest::copy() { - Vector4 v1; - - v1[3] = 1.0f; - - Vector4 v2(v1); - Vector4 v3; - v3[0] = 0.0f; /* this line is here so it's not optimized to Vector4 v3(v1) */ - v3 = v1; - - /* Change original */ - v1[2] = 1.0f; - - /* Verify the copy is the same as original original */ - Vector4 original; - original[3] = 1.0f; - - CORRADE_COMPARE(v2, original); - CORRADE_COMPARE(v3, original); -} - void VectorTest::dot() { CORRADE_COMPARE(Vector4::dot({1.0f, 0.5f, 0.75f, 1.5f}, {2.0f, 4.0f, 1.0f, 7.0f}), 15.25f); } -void VectorTest::multiplyDivide() { - Vector4 vec(1.0f, 2.0f, 3.0f, 4.0f); - Vector4 multiplied(-1.5f, -3.0f, -4.5f, -6.0f); - - CORRADE_COMPARE(vec*-1.5f, multiplied); - CORRADE_COMPARE(-1.5f*vec, multiplied); - CORRADE_COMPARE(multiplied/-1.5f, vec); - - Math::Vector<1, char> vecChar(32); - Math::Vector<1, char> multipliedChar(-48); - CORRADE_COMPARE(vecChar*-1.5f, multipliedChar); - CORRADE_COMPARE(multipliedChar/-1.5f, vecChar); - CORRADE_COMPARE(-1.5f*vecChar, multipliedChar); - - /* Divide vector with number and inverse */ - Vector4 divisor(1.0f, 2.0f, -4.0f, 8.0f); - Vector4 result(1.0f, 0.5f, -0.25f, 0.125f); - CORRADE_COMPARE(1.0f/divisor, result); - CORRADE_COMPARE(-1550.0f/multipliedChar, vecChar); -} - void VectorTest::multiplyDivideComponentWise() { Vector4 vec(1.0f, 2.0f, 3.0f, 4.0f); Vector4 multiplier(7.0f, -4.0f, -1.5f, 1.0f); @@ -133,15 +77,6 @@ void VectorTest::multiplyDivideComponentWise() { CORRADE_COMPARE(multiplied/multiplier, vec); } -void VectorTest::addSubtract() { - Vector4 a(0.5f, -7.5f, 9.0f, -11.0f); - Vector4 b(-0.5, 1.0f, 0.0f, 7.5f); - Vector4 expected(0.0f, -6.5f, 9.0f, -3.5f); - - CORRADE_COMPARE(a + b, expected); - CORRADE_COMPARE(expected - b, a); -} - void VectorTest::dotSelf() { CORRADE_COMPARE(Vector4(1.0f, 2.0f, 3.0f, 4.0f).dot(), 30.0f); } @@ -181,10 +116,6 @@ void VectorTest::angle() { CORRADE_COMPARE(Vector3::angle(Vector3(2.0f, 3.0f, 4.0f).normalized(), Vector3(1.0f, -2.0f, 3.0f).normalized()), rad(1.162514f)); } -void VectorTest::negative() { - CORRADE_COMPARE(-Vector4(1.0f, -3.0f, 5.0f, -10.0f), Vector4(-1.0f, 3.0f, -5.0f, 10.0f)); -} - void VectorTest::debug() { ostringstream o; Debug(&o) << Vector4(0.5f, 15.0f, 1.0f, 1.0f); diff --git a/src/Math/Test/VectorTest.h b/src/Math/Test/VectorTest.h index cde0cafa9..e31af0aa0 100644 --- a/src/Math/Test/VectorTest.h +++ b/src/Math/Test/VectorTest.h @@ -25,12 +25,8 @@ class VectorTest: public Corrade::TestSuite::Tester { void construct(); void constructFrom(); - void data(); - void copy(); void dot(); - void multiplyDivide(); void multiplyDivideComponentWise(); - void addSubtract(); void dotSelf(); void length(); void normalized(); @@ -39,7 +35,6 @@ class VectorTest: public Corrade::TestSuite::Tester { void min(); void max(); void angle(); - void negative(); void debug(); void configuration(); diff --git a/src/Math/Vector.h b/src/Math/Vector.h index c6344ac0b..da1372ee2 100644 --- a/src/Math/Vector.h +++ b/src/Math/Vector.h @@ -19,12 +19,7 @@ * @brief Class Magnum::Math::Vector */ -#include -#include -#include -#include - -#include "MathTypeTraits.h" +#include "RectangularMatrix.h" namespace Magnum { namespace Math { @@ -32,16 +27,6 @@ template class Vector; #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; - }; - /* Implementation for Vector::from(const Vector&) */ template inline constexpr Math::Vector vectorFrom(Sequence, const Math::Vector& vector) { return {T(vector[sequence])...}; @@ -55,28 +40,9 @@ namespace Implementation { @configurationvalueref{Magnum::Math::Vector} @todo Constexprize all for loops */ -template class Vector { - static_assert(size != 0, "Vector cannot have zero elements"); - +template class Vector: public RectangularMatrix<1, s, T> { public: - const static size_t Size = size; /**< @brief %Vector size */ - typedef T Type; /**< @brief %Vector data type */ - - /** - * @brief %Vector from array - * @return Reference to the data as if it was Vector, thus doesn't - * perform any copying. - * - * @attention Use with caution, the function doesn't check whether the - * array is long enough. - */ - inline constexpr static Vector& from(T* data) { - return *reinterpret_cast*>(data); - } - /** @overload */ - inline constexpr static const Vector& from(const T* data) { - return *reinterpret_cast*>(data); - } + const static size_t size = s; /**< @brief %Vector size */ /** * @brief %Vector from another of different type @@ -126,19 +92,15 @@ template class Vector { } /** @brief Default constructor */ - inline constexpr Vector(): _data() {} + inline constexpr Vector() {} /** * @brief Initializer-list constructor * @param first First value * @param next Next values - * - * @todoc Remove workaround when Doxygen supports uniform initialization */ #ifndef DOXYGEN_GENERATING_OUTPUT - template inline constexpr Vector(T first, U... next): _data{first, next...} { - static_assert(sizeof...(next)+1 == size, "Improper number of arguments passed to Vector constructor"); - } + template inline constexpr Vector(T first, U... next): RectangularMatrix<1, size, T>(first, next...) {} #else template inline constexpr Vector(T first, U... next); #endif @@ -153,68 +115,15 @@ template class Vector { inline explicit Vector(T value) { #endif for(size_t i = 0; i != size; ++i) - _data[i] = value; + (*this)[i] = value; } /** @brief Copy constructor */ - inline constexpr Vector(const Vector&) = default; - - /** @brief Assignment operator */ - inline Vector& operator=(const Vector&) = default; - - /** - * @brief Raw data - * @return Array with the same size as the vector - */ - inline T* data() { return _data; } - inline constexpr const T* data() const { return _data; } /**< @overload */ + inline constexpr Vector(const RectangularMatrix<1, size, T>& other): RectangularMatrix<1, size, T>(other) {} /** @brief Value at given position */ - inline T& operator[](size_t pos) { return _data[pos]; } - inline constexpr T operator[](size_t pos) const { return _data[pos]; } /**< @overload */ - - /** @brief Equality operator */ - inline bool operator==(const Vector& other) const { - for(size_t pos = 0; pos != size; ++pos) - if(!MathTypeTraits::equals((*this)[pos], other[pos])) return false; - - return true; - } - - /** @brief Non-equality operator */ - inline bool operator!=(const Vector& other) const { - return !operator==(other); - } - - /** - * @brief Multiply vector - * - * @see operator*=(U), operator*(U, const Vector&) - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - template inline typename std::enable_if::value, Vector>::type operator*(U number) const { - #else - template inline Vector operator*(U number) const { - #endif - return Vector(*this)*=number; - } - - /** - * @brief Multiply vector and assign - * - * More efficient than operator*(U) const, because it does the - * computation in-place. - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - template typename std::enable_if::value, Vector&>::type operator*=(U number) { - #else - template Vector& operator*=(U number) { - #endif - for(size_t i = 0; i != size; ++i) - (*this)[i] *= number; - - return *this; - } + inline T& operator[](size_t pos) { return RectangularMatrix<1, size, T>::_data[pos]; } + inline constexpr T operator[](size_t pos) const { return RectangularMatrix<1, size, T>::_data[pos]; } /**< @overload */ /** * @brief Multiply vector component-wise @@ -238,36 +147,6 @@ template class Vector { return *this; } - /** - * @brief Divide vector - * - * @see operator/=(U), operator/(U, const Vector&) - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - template inline typename std::enable_if::value, Vector>::type operator/(U number) const { - #else - template inline Vector operator/(U number) const { - #endif - return Vector(*this)/=number; - } - - /** - * @brief Divide vector and assign - * - * More efficient than operator/(U) const, because it does the - * computation in-place. - */ - #ifndef DOXYGEN_GENERATING_OUTPUT - template typename std::enable_if::value, Vector&>::type operator/=(U number) { - #else - template Vector& operator/=(U number) { - #endif - for(size_t i = 0; i != size; ++i) - (*this)[i] /= number; - - return *this; - } - /** * @brief Divide vector component-wise * @@ -290,52 +169,6 @@ template class Vector { return *this; } - /** @brief Add two vectors */ - inline Vector operator+(const Vector& other) const { - return Vector(*this)+=other; - } - - /** - * @brief Add and assign vector - * - * More efficient than operator+(), because it does the computation - * in-place. - */ - Vector& operator+=(const Vector& other) { - for(size_t i = 0; i != size; ++i) - (*this)[i] += other[i]; - - return *this; - } - - /** @brief Subtract two vectors */ - inline Vector operator-(const Vector& other) const { - return Vector(*this)-=other; - } - - /** - * @brief Subtract and assign vector - * - * More efficient than operator-(), because it does the computation - * in-place. - */ - Vector& operator-=(const Vector& other) { - for(size_t i = 0; i != size; ++i) - (*this)[i] -= other[i]; - - return *this; - } - - /** @brief Negative vector */ - Vector operator-() const { - Vector out; - - for(size_t i = 0; i != size; ++i) - out[i] = -(*this)[i]; - - return out; - } - /** * @brief Dot product of the vector * @@ -404,45 +237,31 @@ template class Vector { return out; } + #ifndef DOXYGEN_GENERATING_OUTPUT + /* Reimplementation of functions to return correct type */ + template inline RectangularMatrix operator*(const RectangularMatrix& other) const { + return RectangularMatrix<1, size, T>::operator*(other); + } + MAGNUM_RECTANGULARMATRIX_SUBCLASS_IMPLEMENTATION(1, size, Vector) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(1, size, Vector) + #endif + private: - T _data[size]; + /* Hiding unused things from RectangularMatrix */ + using RectangularMatrix<1, size, T>::cols; + using RectangularMatrix<1, size, T>::rows; + using RectangularMatrix<1, size, T>::operator[]; + using RectangularMatrix<1, size, T>::operator(); }; -/** @relates Vector -@brief Multiply number with vector - -@see Vector::operator*(U) const -*/ #ifndef DOXYGEN_GENERATING_OUTPUT template inline typename std::enable_if::value, Vector>::type operator*(U number, const Vector& vector) { -#else -template inline Vector operator*(U number, const Vector& vector) { -#endif - return vector*number; + return number*RectangularMatrix<1, size, T>(vector); } - -/** @relates Vector -@brief Divide vector with number and invert - -Example: -@code -Vector<4, float> vec(1.0f, 2.0f, -4.0f, 8.0f); -Vector<4, float> another = 1.0f/vec; // {1.0f, 0.5f, -0.25f, 0.128f} -@endcode -@see Vector::operator/(U) const -*/ -#ifndef DOXYGEN_GENERATING_OUTPUT -template typename std::enable_if::value, Vector>::type operator/(U number, const Vector& vector) { -#else -template Vector operator/(U number, const Vector& vector) { -#endif - Vector out; - - for(size_t i = 0; i != size; ++i) - out[i] = number/vector[i]; - - return out; +template inline typename std::enable_if::value, Vector>::type operator/(U number, const Vector& vector) { + return number/RectangularMatrix<1, size, T>(vector); } +#endif /** @debugoperator{Magnum::Math::Vector} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Vector& value) { @@ -465,68 +284,41 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili inline constexpr static const Type& from(const T* data) { \ return *reinterpret_cast*>(data); \ } \ - template inline constexpr static Type from(const Vector& other) { \ - return Vector::from(other); \ + template inline constexpr static Type from(const Math::Vector& other) { \ + return Math::Vector::from(other); \ } \ \ inline Type& operator=(const Type& other) { \ - Vector::operator=(other); \ + Math::Vector::operator=(other); \ return *this; \ } \ \ - template inline Type operator*(U number) const { \ - return Vector::operator*(number); \ - } \ - template inline Type& operator*=(U number) { \ - Vector::operator*=(number); \ - return *this; \ - } \ - inline Type operator*(const Vector& other) const { \ - return Vector::operator*(other); \ - } \ - inline Type& operator*=(const Vector& other) { \ - Vector::operator*=(other); \ - return *this; \ + template inline Math::RectangularMatrix operator*(const Math::RectangularMatrix& other) const { \ + return Math::Vector::operator*(other); \ } \ - template inline Type operator/(U number) const { \ - return Vector::operator/(number); \ - } \ - template inline Type& operator/=(U number) { \ - Vector::operator/=(number); \ - return *this; \ - } \ - inline Type operator/(const Vector& other) const { \ - return Vector::operator/(other); \ - } \ - inline Type& operator/=(const Vector& other) { \ - Vector::operator/=(other); \ - return *this; \ - } \ - \ - inline Type operator+(const Vector& other) const { \ - return Vector::operator+(other); \ + inline Type operator*(const Math::Vector& other) const { \ + return Math::Vector::operator*(other); \ } \ - inline Type& operator+=(const Vector& other) { \ - Vector::operator+=(other); \ + inline Type& operator*=(const Math::Vector& other) { \ + Math::Vector::operator*=(other); \ return *this; \ } \ - inline Type operator-(const Vector& other) const { \ - return Vector::operator-(other); \ + inline Type operator/(const Math::Vector& other) const { \ + return Math::Vector::operator/(other); \ } \ - inline Type& operator-=(const Vector& other) { \ - Vector::operator-=(other); \ + inline Type& operator/=(const Math::Vector& other) { \ + Math::Vector::operator/=(other); \ return *this; \ } \ \ - inline Type operator-() const { return Vector::operator-(); } \ - inline Type normalized() const { return Vector::normalized(); } + inline Type normalized() const { return Math::Vector::normalized(); } #define MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Type, size) \ - template inline Type operator*(U number, const Type& vector) { \ - return number*Vector(vector); \ + template inline typename std::enable_if::value, Type>::type operator*(U number, const Type& vector) { \ + return number*Math::Vector(vector); \ } \ - template inline Type operator/(U number, const Type& vector) { \ - return number/Vector(vector); \ + template inline typename std::enable_if::value, Type>::type operator/(U number, const Type& vector) { \ + return number/Math::Vector(vector); \ } #endif diff --git a/src/Math/Vector2.h b/src/Math/Vector2.h index 68d19cb33..d67e8b0c7 100644 --- a/src/Math/Vector2.h +++ b/src/Math/Vector2.h @@ -71,8 +71,8 @@ template class Vector2: public Vector<2, T> { /** @copydoc Vector::Vector(T) */ inline constexpr explicit Vector2(T value = T()): Vector<2, T>(value, value) {} - /** @copydoc Vector::Vector(const Vector&) */ - inline constexpr Vector2(const Vector<2, T>& other): Vector<2, T>(other) {} + /** @brief Copy constructor */ + inline constexpr Vector2(const RectangularMatrix<1, 2, T>& other): Vector<2, T>(other) {} /** * @brief Constructor @@ -88,6 +88,7 @@ template class Vector2: public Vector<2, T> { inline void setY(T value) { (*this)[1] = value; } /**< @brief Set Y component */ MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector2, 2) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(1, 2, Vector2) }; MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Vector2, 2) diff --git a/src/Math/Vector3.h b/src/Math/Vector3.h index 212e79ac4..623bb2c33 100644 --- a/src/Math/Vector3.h +++ b/src/Math/Vector3.h @@ -105,8 +105,8 @@ template class Vector3: public Vector<3, T> { /** @copydoc Vector::Vector(T) */ inline constexpr explicit Vector3(T value): Vector<3, T>(value, value, value) {} - /** @copydoc Vector::Vector(const Vector&) */ - inline constexpr Vector3(const Vector<3, T>& other): Vector<3, T>(other) {} + /** @brief Copy constructor */ + inline constexpr Vector3(const RectangularMatrix<1, 3, T>& other): Vector<3, T>(other) {} /** * @brief Constructor @@ -140,6 +140,7 @@ template class Vector3: public Vector<3, T> { inline constexpr Vector2 xy() const { return Vector2::from(Vector<3, T>::data()); } MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector3, 3) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(1, 3, Vector3) }; MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Vector3, 3) diff --git a/src/Math/Vector4.h b/src/Math/Vector4.h index 303361921..5adfe60ee 100644 --- a/src/Math/Vector4.h +++ b/src/Math/Vector4.h @@ -40,8 +40,8 @@ template class Vector4: public Vector<4, T> { /** @copydoc Vector::Vector(T) */ inline constexpr explicit Vector4(T value): Vector<4, T>(value, value, value, value) {} - /** @copydoc Vector::Vector(const Vector&) */ - inline constexpr Vector4(const Vector<4, T>& other): Vector<4, T>(other) {} + /** @brief Copy constructor */ + inline constexpr Vector4(const RectangularMatrix<1, 4, T>& other): Vector<4, T>(other) {} /** * @brief Constructor @@ -88,6 +88,7 @@ template class Vector4: public Vector<4, T> { inline constexpr Vector2 xy() const { return Vector2::from(Vector<4, T>::data()); } MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector4, 4) + MAGNUM_RECTANGULARMATRIX_SUBCLASS_OPERATOR_IMPLEMENTATION(1, 3, Vector4) }; MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Vector4, 4) diff --git a/src/Mesh.h b/src/Mesh.h index e75567d8b..e02b026c1 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -162,7 +162,8 @@ class MAGNUM_EXPORT Mesh { * @brief Set point size * * @see setProgramPointSize() - * @requires_gl + * @requires_gl Set directly in vertex shader using @c gl_PointSize + * builtin variable. */ inline static void setPointSize(GLfloat size) { glPointSize(size); @@ -174,7 +175,7 @@ class MAGNUM_EXPORT Mesh { * If enabled, the point size is taken from vertex/geometry shader * builtin `gl_PointSize`. * @see setPointSize() - * @requires_gl + * @requires_gl Always enabled. */ inline static void setProgramPointSize(bool enabled) { enabled ? glEnable(GL_PROGRAM_POINT_SIZE) : glDisable(GL_PROGRAM_POINT_SIZE); diff --git a/src/MeshTools/Clean.h b/src/MeshTools/Clean.h index c2e45cddd..76104fcb2 100644 --- a/src/MeshTools/Clean.h +++ b/src/MeshTools/Clean.h @@ -39,7 +39,7 @@ template class Clean { /* Get mesh bounds */ Vertex min, max; - for(size_t i = 0; i != Vertex::Size; ++i) { + for(size_t i = 0; i != Vertex::size; ++i) { min[i] = std::numeric_limits::max(); max[i] = std::numeric_limits::min(); } @@ -53,7 +53,7 @@ template class Clean { /* Make epsilon so large that size_t can index all vertices inside mesh bounds. */ Vertex size = max-min; - for(size_t i = 0; i != Vertex::Size; ++i) + for(size_t i = 0; i != Vertex::size; ++i) if(static_cast(size[i]/std::numeric_limits::max()) > epsilon) epsilon = static_cast(size[i]/std::numeric_limits::max()); @@ -93,7 +93,7 @@ template class Clean { std::swap(newVertices, vertices); /* Move vertex coordinates by epsilon/2 in next direction */ - if(moving != Vertex::Size) { + if(moving != Vertex::size) { moved = Vertex(); moved[moving] = epsilon/2; } @@ -138,7 +138,7 @@ Removes duplicate vertices from the mesh. @todo Interpolate vertices, not collapse them to first in the cell @todo Ability to specify other attributes for interpolation */ -template inline void clean(std::vector& indices, std::vector& vertices, typename Vertex::Type epsilon = TypeTraits::epsilon()) { +template inline void clean(std::vector& indices, std::vector& vertices, typename Vertex::Type epsilon = TypeTraits::epsilon()) { Implementation::Clean(indices, vertices)(epsilon); } diff --git a/src/MeshTools/Test/CleanTest.h b/src/MeshTools/Test/CleanTest.h index f24e4730e..5663733d6 100644 --- a/src/MeshTools/Test/CleanTest.h +++ b/src/MeshTools/Test/CleanTest.h @@ -28,7 +28,7 @@ class CleanTest: public Corrade::TestSuite::Tester { private: class Vector1 { public: - static const size_t Size = 1; + static const size_t size = 1; typedef int Type; Vector1(): data(0) {} diff --git a/src/MeshTools/Test/SubdivideTest.h b/src/MeshTools/Test/SubdivideTest.h index bc7569cb5..af7c43484 100644 --- a/src/MeshTools/Test/SubdivideTest.h +++ b/src/MeshTools/Test/SubdivideTest.h @@ -29,7 +29,7 @@ class SubdivideTest: public Corrade::TestSuite::Tester { private: class Vector1 { public: - static const size_t Size = 1; + static const size_t size = 1; typedef int Type; Vector1(): data(0) {} diff --git a/src/SceneGraph/Camera.cpp b/src/SceneGraph/Camera.cpp index 9afcd4a55..7b8e7d83c 100644 --- a/src/SceneGraph/Camera.cpp +++ b/src/SceneGraph/Camera.cpp @@ -33,7 +33,7 @@ template MatrixType aspectRatioFix(AspectRatioPolicy aspectRat /* Extend on larger side = scale larger side down Clip on smaller side = scale smaller side up */ - return Camera::aspectRatioScale( + return Camera::aspectRatioScale( (relativeAspectRatio.x() > relativeAspectRatio.y()) == (aspectRatioPolicy == AspectRatioPolicy::Extend) ? Vector2(relativeAspectRatio.y()/relativeAspectRatio.x(), 1.0f) : Vector2(1.0f, relativeAspectRatio.x()/relativeAspectRatio.y())); diff --git a/src/Swizzle.h b/src/Swizzle.h index 22239225b..8f30f96a5 100644 --- a/src/Swizzle.h +++ b/src/Swizzle.h @@ -100,7 +100,7 @@ instead of at runtime. @see Vector4::xyz(), Vector4::rgb(), Vector4::xy(), Vector3::xy() */ template inline constexpr typename Implementation::TypeForSize::Type swizzle(const T& vector) { - return {vector[Implementation::GetComponent::value()]...}; + return {vector[Implementation::GetComponent::value()]...}; } /** diff --git a/src/Trade/ImageData.h b/src/Trade/ImageData.h index 587fb0bc9..f054f6464 100644 --- a/src/Trade/ImageData.h +++ b/src/Trade/ImageData.h @@ -70,7 +70,8 @@ template class ImageData: public AbstractImage { inline constexpr const Math::Vector& dimensions() const { return _dimensions; } /** @brief Pointer to raw data */ - inline constexpr const void* data() const { return _data; } + inline void* data() { return _data; } + inline constexpr const void* data() const { return _data; } /**< @overload */ private: std::string _name;