diff --git a/src/Array.h b/src/Array.h new file mode 100644 index 000000000..fc940fc7e --- /dev/null +++ b/src/Array.h @@ -0,0 +1,211 @@ +#ifndef Magnum_Array_h +#define Magnum_Array_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::Array, Magnum::Array1D, Magnum::Array2D, Magnum::Array3D + */ + +#include +#include +#include + +namespace Magnum { + +/** +@brief %Array +@tparam dimensions Dimension count +@tparam T Data type + +Similar to Math::Vector, but more suitable for storing enum values which don't +need any math operations and fuzzy comparison (e.g. enum values). Unlike +Math::Vector this class has non-explicit constructor from one value. +@see Array1D, Array2D, Array3D +*/ +template class Array { + public: + typedef T Type; /**< @brief Data type */ + const static std::uint8_t Dimensions = dimensions; /**< @brief Dimension count */ + + /** + * @brief Default constructor + * + * Sets all components to their default-constructed values + */ + inline constexpr Array(): _data() {} + + /** + * @brief Initializer-list constructor + * @param first First value + * @param next Next values + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template inline constexpr Array(T first, T second, U... next): _data{first, second, next...} { + static_assert(sizeof...(next)+2 == dimensions, "Improper number of arguments passed to Array constructor"); + } + template inline constexpr Array(typename std::enable_if::value && dimensions == 1, U>::type first): _data{first} {} + #else + template inline constexpr Array(T first, U... next); + #endif + + /** + * @brief Constructor + * @param value Value for all fields + */ + template::value && dimensions != 1, U>::type> inline Array(U value) { + for(std::uint8_t i = 0; i != dimensions; ++i) + _data[i] = value; + } + + /** @brief Equality */ + inline bool operator==(const Array& other) const { + for(std::uint8_t i = 0; i != dimensions; ++i) + if(_data[i] != other._data[i]) return false; + return true; + } + + /** @brief Non-equality */ + inline bool operator!=(const Array& other) const { + return !operator==(other); + } + + /** @brief Value at given position */ + inline T& operator[](std::uint8_t pos) { return _data[pos]; } + inline constexpr T operator[](std::uint8_t pos) const { return _data[pos]; } /**< @overload */ + + /** + * @brief Raw data + * @return One-dimensional array of `dimensions` length + */ + inline T* data() { return _data; } + inline constexpr const T* data() const { return _data; } /**< @overload */ + + private: + T _data[dimensions]; +}; + +/** +@brief One-dimensional array +@tparam T Data type +*/ +template class Array1D: public Array<1, T> { + public: + /** @copydoc Array::Array() */ + inline constexpr Array1D() = default; + + /** + * @brief Constructor + * @param x X component + */ + inline constexpr Array1D(T x): Array<1, T>(x) {} + + /** @brief Copy constructor */ + inline constexpr Array1D(const Array<1, T>& other): Array<1, T>(other) {} + + inline T& x() { return (*this)[0]; } /**< @brief X component */ + inline constexpr T x() const { return (*this)[0]; } /**< @overload */ +}; + +/** +@brief Two-dimensional array +@tparam T Data type +*/ +template class Array2D: public Array<2, T> { + public: + /** @copydoc Array::Array() */ + inline constexpr Array2D() = default; + + /** + * @brief Constructor + * @param x X component + * @param y Y component + */ + inline constexpr Array2D(T x, T y): Array<2, T>(x, y) {} + + /** @copydoc Array::Array(U) */ + inline constexpr Array2D(T value): Array<2, T>(value, value) {} + + /** @brief Copy constructor */ + inline constexpr Array2D(const Array<2, T>& other): Array<2, T>(other) {} + + inline T& x() { return (*this)[0]; } /**< @brief X component */ + inline constexpr T x() const { return (*this)[0]; } /**< @overload */ + inline T& y() { return (*this)[1]; } /**< @brief Y component */ + inline constexpr T y() const { return (*this)[1]; } /**< @overload */ +}; + +/** +@brief Three-dimensional array +@tparam T Data type +*/ +template class Array3D: public Array<3, T> { + public: + /** @copydoc Array::Array() */ + inline constexpr Array3D() {} + + /** + * @brief Constructor + * @param x X component + * @param y Y component + * @param z Z component + */ + inline constexpr Array3D(T x, T y, T z): Array<3, T>(x, y, z) {} + + /** @copydoc Array::Array(U) */ + inline constexpr Array3D(T value): Array<3, T>(value, value, value) {} + + /** @brief Copy constructor */ + inline constexpr Array3D(const Array<3, T>& other): Array<3, T>(other) {} + + inline T& x() { return (*this)[0]; } /**< @brief X component */ + inline constexpr T x() const { return (*this)[0]; } /**< @overload */ + inline T& y() { return (*this)[1]; } /**< @brief Y component */ + inline constexpr T y() const { return (*this)[1]; } /**< @overload */ + inline T& z() { return (*this)[2]; } /**< @brief Z component */ + inline constexpr T z() const { return (*this)[2]; } /**< @overload */ +}; + +/** @debugoperator{Magnum::Array} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Array& value) { + debug << "Array("; + debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); + for(std::uint8_t i = 0; i != dimensions; ++i) { + if(i != 0) debug << ", "; + debug << value[i]; + } + debug << ')'; + debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, true); + return debug; +} + +/** @debugoperator{Magnum::Array1D} */ +template inline Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Array1D& value) { + return debug << static_cast&>(value); +} + +/** @debugoperator{Magnum::Array2D} */ +template inline Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Array2D& value) { + return debug << static_cast&>(value); +} + +/** @debugoperator{Magnum::Array3D} */ +template inline Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Array3D& value) { + return debug << static_cast&>(value); +} + +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4e9051cd8..a9d4b9d59 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(Magnum_HEADERS AbstractResourceLoader.h AbstractShaderProgram.h AbstractTexture.h + Array.h Buffer.h Color.h Context.h diff --git a/src/Magnum.h b/src/Magnum.h index 6635feb97..3e5da96e6 100644 --- a/src/Magnum.h +++ b/src/Magnum.h @@ -107,6 +107,12 @@ using Math::rad; class AbstractImage; class AbstractShaderProgram; class AbstractTexture; + +template class Array; +template class Array1D; +template class Array2D; +template class Array3D; + class Buffer; #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Test/ArrayTest.cpp b/src/Test/ArrayTest.cpp new file mode 100644 index 000000000..928ecc274 --- /dev/null +++ b/src/Test/ArrayTest.cpp @@ -0,0 +1,92 @@ +/* + 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 "ArrayTest.h" + +#include "Array.h" + +CORRADE_TEST_MAIN(Magnum::Test::ArrayTest) + +namespace Magnum { namespace Test { + +typedef Magnum::Array1D Array1D; +typedef Magnum::Array2D Array2D; +typedef Magnum::Array3D Array3D; + +ArrayTest::ArrayTest() { + addTests(&ArrayTest::construct, + &ArrayTest::constexprConstruct, + &ArrayTest::equality, + &ArrayTest::access); +} + +void ArrayTest::construct() { + CORRADE_COMPARE(Array1D(5), (Array<1, int>(5))); + CORRADE_COMPARE(Array2D(5, 3), (Array<2, int>(5, 3))); + CORRADE_COMPARE(Array3D(5, 3, -2), (Array<3, int>(5, 3, -2))); + + /* Verify proper expansion */ + CORRADE_COMPARE((Array<3, int>(5)), (Array<3, int>(5, 5, 5))); + CORRADE_COMPARE(Array2D(5), (Array<2, int>(5, 5))); + CORRADE_COMPARE(Array3D(5), (Array<3, int>(5, 5, 5))); +} + +void ArrayTest::constexprConstruct() { + /* Verify that all full constructors can be called as constexpr */ + constexpr Array1D a(5); + constexpr Array2D b(5, 3); + constexpr Array2D b2(5); + constexpr Array3D c(5, 6, 7); + constexpr Array3D c2(5); + constexpr Array<3, int> d(5, 6, 7); + + CORRADE_COMPARE(a, Array1D(5)); + CORRADE_COMPARE(b, Array2D(5, 3)); + CORRADE_COMPARE(b2, Array2D(5)); + CORRADE_COMPARE(c, Array3D(5, 6, 7)); + CORRADE_COMPARE(c2, Array3D(5)); + CORRADE_COMPARE(d, (Array<3, int>(5, 6, 7))); +} + +void ArrayTest::equality() { + CORRADE_VERIFY((Array<3, int>(5, 6, 7) == Array<3, int>(5, 6, 7))); + CORRADE_VERIFY((Array<3, int>(5, 6, 7) != Array<3, int>(5, 6, 8))); +} + +void ArrayTest::access() { + Array1D a(50); + const Array1D ac(50); + Array2D b(5, 3); + const Array2D bc(5, 3); + Array3D c(-5, 6, 7); + const Array3D cc(-5, 6, 7); + + CORRADE_COMPARE(a.x(), 50); + CORRADE_COMPARE(ac.x(), 50); + + CORRADE_COMPARE(b.x(), 5); + CORRADE_COMPARE(b.y(), 3); + CORRADE_COMPARE(bc.x(), 5); + CORRADE_COMPARE(bc.y(), 3); + + CORRADE_COMPARE(c.x(), -5); + CORRADE_COMPARE(c.y(), 6); + CORRADE_COMPARE(c.z(), 7); + CORRADE_COMPARE(cc.x(), -5); + CORRADE_COMPARE(cc.y(), 6); + CORRADE_COMPARE(cc.z(), 7); +} + +}} diff --git a/src/Test/ArrayTest.h b/src/Test/ArrayTest.h new file mode 100644 index 000000000..f90059f40 --- /dev/null +++ b/src/Test/ArrayTest.h @@ -0,0 +1,34 @@ +#ifndef Magnum_Test_ArrayTest_h +#define Magnum_Test_ArrayTest_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 Test { + +class ArrayTest: public Corrade::TestSuite::Tester { + public: + ArrayTest(); + + void construct(); + void constexprConstruct(); + void equality(); + void access(); +}; + +}} + +#endif diff --git a/src/Test/CMakeLists.txt b/src/Test/CMakeLists.txt index 2e6f58e3e..68ebd975e 100644 --- a/src/Test/CMakeLists.txt +++ b/src/Test/CMakeLists.txt @@ -1,3 +1,4 @@ +corrade_add_test2(ArrayTest ArrayTest.cpp) corrade_add_test2(ColorTest ColorTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test2(MeshTest MeshTest.cpp LIBRARIES Magnum) corrade_add_test2(ResourceManagerTest ResourceManagerTest.cpp LIBRARIES MagnumTestLib)