From c256523102f4a2da2f30d28d110503ac2ee5b401 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 21 Nov 2020 00:58:46 +0700 Subject: [PATCH] basic matrix type --- headers/vmath.hpp/vmath.hpp | 1 + headers/vmath.hpp/vmath_fwd.hpp | 18 +++ headers/vmath.hpp/vmath_mat.hpp | 223 ++++++++++++++++++++++++++++++++ untests/vmath_mat_tests.cpp | 204 +++++++++++++++++++++++++++++ 4 files changed, 446 insertions(+) create mode 100644 headers/vmath.hpp/vmath_mat.hpp create mode 100644 untests/vmath_mat_tests.cpp diff --git a/headers/vmath.hpp/vmath.hpp b/headers/vmath.hpp/vmath.hpp index 88a871e..cf975dc 100644 --- a/headers/vmath.hpp/vmath.hpp +++ b/headers/vmath.hpp/vmath.hpp @@ -6,4 +6,5 @@ #pragma once +#include "vmath_mat.hpp" #include "vmath_vec.hpp" diff --git a/headers/vmath.hpp/vmath_fwd.hpp b/headers/vmath.hpp/vmath_fwd.hpp index 31621fe..30ff35f 100644 --- a/headers/vmath.hpp/vmath_fwd.hpp +++ b/headers/vmath.hpp/vmath_fwd.hpp @@ -35,3 +35,21 @@ namespace vmath_hpp using vec3f = vec; using vec4f = vec; } + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + class mat; + + using mat2i = mat; + using mat3i = mat; + using mat4i = mat; + + using mat2b = mat; + using mat3b = mat; + using mat4b = mat; + + using mat2f = mat; + using mat3f = mat; + using mat4f = mat; +} diff --git a/headers/vmath.hpp/vmath_mat.hpp b/headers/vmath.hpp/vmath_mat.hpp new file mode 100644 index 0000000..2acbdbd --- /dev/null +++ b/headers/vmath.hpp/vmath_mat.hpp @@ -0,0 +1,223 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "vmath_fwd.hpp" +#include "vmath_vec.hpp" + +namespace vmath_hpp::detail +{ + template < typename T, std::size_t Size > + class mat_base; + + template < typename T > + class mat_base { + public: + vec rows[2]; + public: + constexpr mat_base() : rows{ + {1, 0}, + {0, 1}, + } {} + + constexpr explicit mat_base(uninit_t) {} + + constexpr mat_base( + T m11, T m12, + T m21, T m22) + : rows{ + {m11, m12}, + {m21, m22}} {} + + constexpr mat_base( + const vec& row0, + const vec& row1) + : rows{row0, row1} {} + }; + + template < typename T > + class mat_base { + public: + vec rows[3]; + public: + constexpr mat_base() : rows{ + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + } {} + + constexpr explicit mat_base(uninit_t) {} + + constexpr mat_base( + T m11, T m12, T m13, + T m21, T m22, T m23, + T m31, T m32, T m33) + : rows{ + {m11, m12, m13}, + {m21, m22, m23}, + {m31, m32, m33}} {} + + constexpr mat_base( + const vec& row0, + const vec& row1, + const vec& row2) + : rows{row0, row1, row2} {} + }; + + template < typename T > + class mat_base { + public: + vec rows[4]; + public: + constexpr mat_base() : rows{ + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1}, + } {} + + constexpr explicit mat_base(uninit_t) {} + + constexpr mat_base( + T m11, T m12, T m13, T m14, + T m21, T m22, T m23, T m24, + T m31, T m32, T m33, T m34, + T m41, T m42, T m43, T m44) + : rows{ + {m11, m12, m13, m14}, + {m21, m22, m23, m24}, + {m31, m32, m33, m34}, + {m41, m42, m43, m44}} {} + + constexpr mat_base( + const vec& row0, + const vec& row1, + const vec& row2, + const vec& row3) + : rows{row0, row1, row2, row3} {} + }; +} + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + class mat final : public detail::mat_base { + public: + using self_type = mat; + using base_type = detail::mat_base; + using value_type = vec; + + using pointer = value_type*; + using const_pointer = const value_type*; + + using reference = value_type&; + using const_reference = const value_type&; + + using iterator = value_type*; + using const_iterator = const value_type*; + + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + public: + using base_type::rows; + using base_type::mat_base; + + mat(mat&&) = default; + mat& operator=(mat&&) = default; + + mat(const mat&) = default; + mat& operator=(const mat&) = default; + + void swap(mat& other) noexcept(std::is_nothrow_swappable_v) { + for ( std::size_t i = 0; i < Size; ++i ) { + using std::swap; + swap(rows[i], other.rows[i]); + } + } + + constexpr iterator begin() noexcept { return iterator(rows); } + constexpr const_iterator begin() const noexcept { return const_iterator(rows); } + constexpr const_iterator cbegin() const noexcept { return const_iterator(rows); } + + constexpr iterator end() noexcept { return iterator(rows + Size); } + constexpr const_iterator end() const noexcept { return const_iterator(rows + Size); } + constexpr const_iterator cend() const noexcept { return const_iterator(rows + Size); } + + constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } + + constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } + + constexpr size_type size() const noexcept { return Size; } + constexpr size_type max_size() const noexcept { return Size; } + constexpr bool empty() const noexcept { return !Size; } + + constexpr value_type& operator[](size_type index) noexcept { + return rows[index]; + } + + constexpr const value_type& operator[](size_type index) const noexcept { + return rows[index]; + } + + constexpr value_type& at(size_type index) { + if ( index < Size ) { + return rows[index]; + } + throw std::out_of_range("mat::at"); + } + + constexpr const value_type& at(size_type index) const { + if ( index < Size ) { + return rows[index]; + } + throw std::out_of_range("mat::at"); + } + }; + + template < typename T, std::size_t Size > + void swap(mat& l, mat& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } +} + +namespace vmath_hpp +{ + template < typename T, std::size_t Size > + constexpr bool operator==(const mat& l, const mat& r) { + for ( std::size_t i = 0; i < Size; ++i ) { + if ( !(l[i] == r[i]) ) { + return false; + } + } + return true; + } + + template < typename T, std::size_t Size > + constexpr bool operator!=(const mat& l, const mat& r) { + return !(l == r); + } + + template < typename T, std::size_t Size > + constexpr bool operator<(const mat& l, const mat& r) { + for ( std::size_t i = 0; i < Size; ++i ) { + if ( l[i] < r[i] ) { + return true; + } + if ( r[i] < l[i] ) { + return false; + } + } + return false; + } +} diff --git a/untests/vmath_mat_tests.cpp b/untests/vmath_mat_tests.cpp new file mode 100644 index 0000000..53f3b10 --- /dev/null +++ b/untests/vmath_mat_tests.cpp @@ -0,0 +1,204 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/vmath.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2020, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include + +#define CATCH_CONFIG_FAST_COMPILE +#include + +namespace +{ +} + +TEST_CASE("vmath/mat") { + using namespace vmath_hpp; + + SECTION("sizeof") { + STATIC_REQUIRE(sizeof(mat2i{}) == sizeof(int) * 2 * 2); + STATIC_REQUIRE(sizeof(mat3i{}) == sizeof(int) * 3 * 3); + STATIC_REQUIRE(sizeof(mat4i{}) == sizeof(int) * 4 * 4); + } + + SECTION("ctors") { + { + STATIC_REQUIRE(mat2i()[0] == vec2i(1,0)); + STATIC_REQUIRE(mat2i()[1] == vec2i(0,1)); + + STATIC_REQUIRE(mat2i(1,2,3,4)[0] == vec2i(1,2)); + STATIC_REQUIRE(mat2i(1,2,3,4)[1] == vec2i(3,4)); + + STATIC_REQUIRE(mat2i({1,2},{3,4})[0] == vec2i(1,2)); + STATIC_REQUIRE(mat2i({1,2},{3,4})[1] == vec2i(3,4)); + } + { + constexpr mat2i v(1,2,3,4); + constexpr mat2i v2 = v; + STATIC_REQUIRE(v2 == mat2i(1,2,3,4)); + } + { + constexpr mat2i v(1,2,3,4); + constexpr mat2i v2 = std::move(v); + STATIC_REQUIRE(v2 == mat2i(1,2,3,4)); + } + } + + SECTION("operator=") { + { + mat2i v(1,2,3,4); + mat2i v2; + v2 = v; + REQUIRE(v2 == mat2i(1,2,3,4)); + } + { + mat2i v(1,2,3,4); + mat2i v2; + v2 = std::move(v); + REQUIRE(v2 == mat2i(1,2,3,4)); + } + } + + SECTION("swap") { + { + mat2i v1(1,2,3,4); + mat2i v2(4,5,6,7); + v1.swap(v2); + REQUIRE(v1 == mat2i(4,5,6,7)); + REQUIRE(v2 == mat2i(1,2,3,4)); + } + { + mat2i v1(1,2,3,4); + mat2i v2(4,5,6,7); + swap(v1, v2); + REQUIRE(v1 == mat2i(4,5,6,7)); + REQUIRE(v2 == mat2i(1,2,3,4)); + } + } + + SECTION("iter") { + { + mat2i v{1,2,3,4}; + mat2i::iterator i = v.begin(); + REQUIRE(*i++ == vec2i{1,2}); + REQUIRE(*i++ == vec2i{3,4}); + REQUIRE(v.end() == i); + } + { + const mat2i v{1,2,3,4}; + mat2i::const_iterator i = v.begin(); + REQUIRE(*i++ == vec2i{1,2}); + REQUIRE(*i++ == vec2i{3,4}); + REQUIRE(v.end() == i); + } + { + mat2i v{1,2,3,4}; + for ( auto i = v.begin(); i < v.end(); ++i ) { + *i = vec2i(i->x() * 2, i->y() * 2); + } + REQUIRE(v == mat2i(2,4,6,8)); + } + } + + SECTION("riter") { + { + mat2i v{1,2,3,4}; + mat2i::reverse_iterator i = v.rbegin(); + REQUIRE(*i++ == vec2i{3,4}); + REQUIRE(*i++ == vec2i{1,2}); + REQUIRE(v.rend() == i); + for ( auto j = v.rbegin(); j < v.rend(); ++j ) { + *j = vec2i(j->x() * 2, j->y() * 2); + } + REQUIRE(v == mat2i(2,4,6,8)); + } + { + const mat2i v{1,2,3,4}; + mat2i::const_reverse_iterator i = v.rbegin(); + REQUIRE(*i++ == vec2i{3,4}); + REQUIRE(*i++ == vec2i{1,2}); + REQUIRE(v.rend() == i); + } + { + mat2i v{1,2,3,4}; + for ( auto i = v.rbegin(); i < v.rend(); ++i ) { + *i = vec2i(i->x() * 2, i->y() * 2); + } + REQUIRE(v == mat2i(2,4,6,8)); + } + } + + SECTION("size/max_size/empty") { + STATIC_REQUIRE(mat().size() == 2); + STATIC_REQUIRE(mat().max_size() == 2); + STATIC_REQUIRE_FALSE(mat().empty()); + + STATIC_REQUIRE(mat().size() == 3); + STATIC_REQUIRE(mat().max_size() == 3); + STATIC_REQUIRE_FALSE(mat().empty()); + } + + SECTION("operator[]") { + { + STATIC_REQUIRE(mat2i()[0] == vec2i(1,0)); + STATIC_REQUIRE(mat2i()[1] == vec2i(0,1)); + + STATIC_REQUIRE(mat3i()[0] == vec3i(1,0,0)); + STATIC_REQUIRE(mat3i()[1] == vec3i(0,1,0)); + STATIC_REQUIRE(mat3i()[2] == vec3i(0,0,1)); + + STATIC_REQUIRE(mat4i()[0] == vec4i(1,0,0,0)); + STATIC_REQUIRE(mat4i()[1] == vec4i(0,1,0,0)); + STATIC_REQUIRE(mat4i()[2] == vec4i(0,0,1,0)); + STATIC_REQUIRE(mat4i()[3] == vec4i(0,0,0,1)); + } + { + mat2i v; + v[0] = vec2i(1,2); + v[1] = vec2i(3,4); + REQUIRE(v == mat2i(1,2,3,4)); + } + { + mat3i v; + v[0] = vec3i(1,2,3); + v[1] = vec3i(4,5,6); + v[2] = vec3i(7,8,9); + REQUIRE(v == mat3i(1,2,3,4,5,6,7,8,9)); + } + { + mat4i v; + v[0] = vec4i(1,2,3,4); + v[1] = vec4i(5,6,7,8); + v[2] = vec4i(9,10,11,12); + v[3] = vec4i(13,14,15,16); + REQUIRE(v == mat4i(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)); + } + } + + SECTION("at") { + STATIC_REQUIRE(mat2i(1,2,3,4).at(0) == vec2i(1,2)); + STATIC_REQUIRE(mat2i(1,2,3,4).at(1) == vec2i(3,4)); + REQUIRE_THROWS_AS(mat2i(1,2,3,4).at(2), std::out_of_range); + } + + SECTION("operator==/operator!=") { + STATIC_REQUIRE(mat2i(1,2,3,4) == mat2i(1,2,3,4)); + STATIC_REQUIRE_FALSE(mat2i(1,2,3,4) == mat2i(2,2,3,4)); + STATIC_REQUIRE_FALSE(mat2i(1,2,3,4) == mat2i(1,3,3,4)); + + STATIC_REQUIRE_FALSE(mat2i(1,2,3,4) != mat2i(1,2,3,4)); + STATIC_REQUIRE(mat2i(1,2,3,4) != mat2i(2,2,3,4)); + STATIC_REQUIRE(mat2i(1,2,3,4) != mat2i(1,3,3,4)); + } + + SECTION("operator<") { + STATIC_REQUIRE_FALSE(mat2i(1,2,3,4) < mat2i(1,2,3,4)); + + STATIC_REQUIRE(mat2i(1,1,3,4) < mat2i(1,2,3,4)); + STATIC_REQUIRE_FALSE(mat2i(1,2,3,4) < mat2i(1,1,3,4)); + + STATIC_REQUIRE(mat2i(0,3,3,4) < mat2i(1,2,3,4)); + STATIC_REQUIRE_FALSE(mat2i(1,2,3,4) < mat2i(0,3,3,4)); + } +}