more of the new matrix vector implementation

This commit is contained in:
Hartmut Seichter 2024-07-11 23:38:43 +02:00
parent 63135466a2
commit 3989c0f68e
7 changed files with 293 additions and 48 deletions

View file

@ -25,6 +25,8 @@
#include <cmath>
#include <algorithm>
#include <numbers>
//#include <pw/core/matrix.hpp>
//#include <pw/core/quaternion.hpp>

View file

@ -26,9 +26,193 @@
#include <pw/core/globals.hpp>
#include <pw/core/math.hpp>
#include <pw/core/vector.hpp>
#include <utility>
namespace pw {
template <typename Scalar, unsigned int Rows, unsigned int Cols>
struct matrix final {
static_assert(Rows > 0 && Cols > 0, "undefined matrix type");
static constexpr unsigned int diag_size{std::min(Rows, Cols)};
static constexpr unsigned int rows{Rows};
static constexpr unsigned int cols{Cols};
static constexpr bool is_square{Rows == Cols};
using diag_type = vector<Scalar, diag_size>;
using transpose_type = matrix<Scalar, Cols, Rows>;
using minor_type = matrix<Scalar, Cols - 1, Rows - 1>;
using column_type = vector<Scalar, Rows>;
vector<vector<Scalar, Cols>, Rows> m_{};
auto&& data(this auto&& self) {
return std::forward<decltype(self)>(self).m_[0].data();
}
auto&& operator[](this auto&& self, int r) {
return std::forward<decltype(self)>(self).m_[r];
}
constexpr auto diagonal() const noexcept -> diag_type {
return [this]<std::size_t... Is>(std::index_sequence<Is...>) {
return diag_type{(*this)[Is][Is]...};
}(std::make_index_sequence<diag_size>{});
}
constexpr auto trace() const noexcept -> Scalar {
static_assert(Rows == Cols,
"trace is only defined for square matrices");
return [this, sum = Scalar{0}]<std::size_t... Is>(
std::index_sequence<Is...>) mutable {
return ((*this)[Is][Is] + ... + sum);
}(std::make_index_sequence<diag_size>{});
}
constexpr auto
column(std::unsigned_integral auto c) const noexcept -> column_type {
return [this, &c]<std::size_t... Rs>(std::index_sequence<Rs...>) {
return column_type{(*this)[Rs][c]...};
}(std::make_index_sequence<Cols>{});
}
constexpr auto transposed() const noexcept -> transpose_type {
return [this]<std::size_t... Cs>(std::index_sequence<Cs...>) {
return transpose_type{(*this).column(Cs)...};
}(std::make_index_sequence<Cols>{});
}
static constexpr auto all(const Scalar& value) {
return [&value]<std::size_t... Rs>(std::index_sequence<Rs...>) {
return matrix{vector<Scalar, Cols>::all((value) + Rs - Rs)...};
}(std::make_index_sequence<Rows>{});
}
static constexpr auto identity() noexcept -> matrix {
static_assert(Rows == Cols,
"identity only defined for square matrices");
return []<std::size_t... Rs>(std::index_sequence<Rs...>) {
return matrix{vector<Scalar, Cols>::basis(Rs)...};
}(std::make_index_sequence<Rows>{});
}
constexpr auto minor(std::unsigned_integral auto r0,
std::unsigned_integral auto c0) const noexcept {
static_assert(Rows > 1 && Cols > 1, "cannot create minor matrix");
return [this, &r0, &c0]<std::size_t... Rs>(std::index_sequence<Rs...>) {
return matrix<Scalar, Rows - 1, Cols - 1>{
(*this)[(Rs < r0) ? Rs : Rs + 1].minor(c0)...};
}(std::make_index_sequence<Rows - 1>{});
}
constexpr auto determinant() const noexcept -> Scalar {
static_assert(Rows == Cols,
"determinant only defined for square matrices");
return [this]<std::size_t... Cs>(std::index_sequence<Cs...>) {
return ((((Cs % 2 == 0) ? (*this)[0][Cs] : -(*this)[0][Cs]) *
(*this).minor(0u, Cs).determinant()) +
...);
}(std::make_index_sequence<Cols>{});
}
constexpr auto inverse() const noexcept -> matrix {
const auto inv_det{Scalar(1) / this->determinant()};
matrix<Scalar, Rows, Cols> inv; // no initialization needed!
for (auto c = 0u; c < Cols; c++) {
for (auto r = 0u; r < Rows; r++) {
const auto min_det = this->minor(r, c).determinant();
const auto co_fact{((r + c) % 2 == 1) ? -min_det : min_det};
inv[r][c] = inv_det * co_fact;
}
}
return inv;
}
constexpr matrix operator*(const Scalar& v) const noexcept {
return [this, &v]<std::size_t... Rs>(std::index_sequence<Rs...>) {
return matrix{((*this)[Rs] * v)...};
}(std::make_index_sequence<Rows>{});
}
constexpr matrix operator/(const Scalar& v) const noexcept {
return operator*(Scalar{1} / v);
}
constexpr matrix operator+(const Scalar& v) const noexcept {
return [this, &v]<std::size_t... Rs>(std::index_sequence<Rs...>) {
return matrix{((*this)[Rs] + v)...};
}(std::make_index_sequence<Rows>{});
}
constexpr matrix operator-(const Scalar& v) const noexcept {
return operator+(-v);
}
constexpr auto operator*=(const Scalar& v) noexcept {
[this, &v]<std::size_t... Rs>(std::index_sequence<Rs...>) {
(((*this)[Rs] *= v), ...);
}(std::make_index_sequence<Rows>{});
return *this;
}
constexpr auto operator/=(const Scalar& v) noexcept {
return operator*=(Scalar{1} / v);
}
constexpr auto operator+=(const Scalar& v) noexcept {
[this, &v]<std::size_t... Rs>(std::index_sequence<Rs...>) {
(((*this)[Rs] += v), ...);
}(std::make_index_sequence<Rows>{});
return *this;
}
constexpr auto operator-=(const Scalar& v) noexcept {
return operator+=(-v);
}
};
template <> constexpr float matrix<float, 1, 1>::determinant() const noexcept {
return (*this)[0][0];
}
template <>
constexpr double matrix<double, 1, 1>::determinant() const noexcept {
return (*this)[0][0];
}
template <typename Scalar, unsigned int Ra, unsigned int CaRb, unsigned int Cb>
constexpr auto operator*(const matrix<Scalar, Ra, CaRb>& A,
const matrix<Scalar, CaRb, Cb>& B) noexcept {
matrix<Scalar, Ra, Cb> result{};
[&]<std::size_t... Ia>(std::index_sequence<Ia...>) {
(
[&]<std::size_t... Ib>(auto&& Ai, std::index_sequence<Ib...>) {
(
[&]<std::size_t... iI>(auto&& Ai, auto&& Bi,
std::index_sequence<iI...>) {
((result[Ai][Bi] += A[Ai][iI] * B[iI][Bi]),
...); // do the actual multiplication
}(Ai, Ib, std::make_index_sequence<CaRb>{}),
...); // forward row of A, colum of B and unpack inner loop
}(Ia, std::make_index_sequence<Cb>{}),
...); // forward current row, unpack A
}(std::make_index_sequence<Ra>{}); // rows of A as input
return result;
}
template <typename ScalarA, typename ScalarB, unsigned int Ra, unsigned int Ca>
constexpr auto operator*(const matrix<ScalarA, Ra, Ca>& A,
const vector<ScalarB, Ca>& B) noexcept {
// first step - should move to concepts to allow for std::array and
// std::span to be allowed
vector<std::common_type_t<ScalarA, ScalarB>, Ca> result{};
[&]<std::size_t... Ia>(std::index_sequence<Ia...>) {
(
[&]<std::size_t... Ib>(auto&& Ai, std::index_sequence<Ib...>) {
((result[Ib] += A[Ai][Ib] * B[Ib]), ...);
}(Ia, std::make_index_sequence<Ca>{}),
...); // forward current row, unpack A
}(std::make_index_sequence<Ra>{}); // rows of A as input
return result;
}
} // namespace pw

View file

@ -34,9 +34,6 @@ struct point_ {
T_ x {0}, y {0};
point_() = default;
point_(T_ x_,T_ y_) : x(x_), y(y_) {}
template <typename To_>
point_<To_> cast() const { return point_<To_>(static_cast<To_>(x),static_cast<To_>(y)); }

View file

@ -23,6 +23,8 @@
#ifndef PW_CORE_VECTOR_HPP
#define PW_CORE_VECTOR_HPP
#include <pw/core/globals.hpp>
namespace pw {
template <typename Scalar, unsigned int size> struct vector final {
@ -112,10 +114,56 @@ template <typename Scalar, unsigned int size> struct vector final {
return vector{{(*this)[Ss] - v[Ss]...}};
}(std::make_index_sequence<size>{});
}
constexpr vector& operator*=(const Scalar& v) noexcept {
[this, &v]<std::size_t... Ss>(std::index_sequence<Ss...>) {
(((*this)[Ss] *= v), ...);
}(std::make_index_sequence<size>{});
return *this;
}
constexpr vector& operator/=(const Scalar& v) noexcept {
return operator*=(Scalar{1} / v);
}
constexpr vector& operator+=(const Scalar& v) noexcept {
[this, &v]<std::size_t... Ss>(std::index_sequence<Ss...>) {
(((*this)[Ss] += v), ...);
}(std::make_index_sequence<size>{});
return *this;
}
constexpr vector& operator-=(const Scalar& v) noexcept {
return operator+=(-v);
}
constexpr auto project() const noexcept -> vector<Scalar, size - 1> {
return [this]<std::size_t... Ss>(std::index_sequence<Ss...>) {
return vector<Scalar, size - 1>{
{(*this)[Ss] / (*this)[size - 1]...}};
}(std::make_index_sequence<size - 1>{});
}
constexpr auto
unproject(auto&& w) const noexcept -> vector<Scalar, size + 1> {
return [this, &w]<std::size_t... Ss>(std::index_sequence<Ss...>) {
return vector<Scalar, size + 1>{{(Ss < size) ? (*this)[Ss] : w...}};
}(std::make_index_sequence<size + 1>{});
}
template <typename ScalarCast>
constexpr auto cast() const noexcept {
return [this]<std::size_t... Ss>(std::index_sequence<Ss...>) {
return vector{ ScalarCast{(*this)[Ss]}...};
}(std::make_index_sequence<size>{});
}
};
template <typename Scalar>
struct vector2 : vector<Scalar, 2> {
// deduction guide for vector
template <class... U, class CT = std::common_type_t<U...>>
vector(U...) -> vector<CT, sizeof...(U)>;
template <typename Scalar> struct vector2 : vector<Scalar, 2> {
constexpr const Scalar& x() const { return (*this)[0]; }
constexpr Scalar& x() { return (*this)[0]; }
@ -159,18 +207,15 @@ template <typename Scalar> struct vector4 : vector<Scalar, 4> {
constexpr const Scalar& w() const { return (*this)[3]; }
constexpr Scalar& w() { return (*this)[3]; }
constexpr auto xyz() const { return base_type::swizzle(0,1,2); }
constexpr auto xyz() const { return base_type::swizzle(0, 1, 2); }
};
using vector2f = vector2<float>;
using vector2d = vector2<double>;
using vector2i = vector2<int>;
using vector2f = vector2<float>;
using vector2d = vector2<double>;
using vector2i = vector2<int>;
using vector3f = vector3<float>;
using vector3d = vector3<double>;
using vector3f = vector3<float>;
using vector3d = vector3<double>;
} // namespace pw

View file

@ -7,8 +7,8 @@ macro(make_test arg1)
endmacro()
# make_test(pwcore_test_matrix)
# make_test(pwcore_test_vector)
make_test(pwcore_test_matrix)
make_test(pwcore_test_vector)
# make_test(pwcore_test_axisangle)
# make_test(pwcore_test_quaternion)
# make_test(pwcore_test_transform_tools)

View file

@ -10,12 +10,9 @@ set(pixwerx_test_files
pw_test_core.cpp
)
add_executable(pixwerx_tests ${pixwerx_test_files})
target_link_libraries(pixwerx_tests PRIVATE
target_link_libraries(pixwerx_tests PRIVATE
snitch::snitch
pwcore
)

View file

@ -3,42 +3,55 @@
#include <pw/core/matrix.hpp>
#include <pw/core/vector.hpp>
TEST_CASE("core", "[matrix]") {
auto m44 = pw::matrix<float,2,2>{};
auto mat = pw::matrix<float,2,2>{};
REQUIRE(m44[0][0] == 0);
REQUIRE(m44[0][1] == 0);
REQUIRE(m44[1][0] == 0);
REQUIRE(m44[1][1] == 0);
REQUIRE(mat[0][0] == 0);
REQUIRE(mat[0][1] == 0);
REQUIRE(mat[1][0] == 0);
REQUIRE(mat[1][1] == 0);
// auto m44_1 = pw::matrix<2, 2, float>{ 5, 4, 3, 2 };
auto mat_1 = pw::matrix<float,2, 2>{
pw::vector{5.f, 4},
pw::vector{3.f, 2}
};
// REQUIRE(m44_1(0, 0) == 5);
// REQUIRE(m44_1(0, 1) == 3);
// REQUIRE(m44_1(1, 0) == 4);
// REQUIRE(m44_1(1, 1) == 2);
REQUIRE(mat_1[0][0] == 5);
REQUIRE(mat_1[0][1] == 4);
REQUIRE(mat_1[1][0] == 3);
REQUIRE(mat_1[1][1] == 2);
// auto m44_2 = pw::matrix2x2_<float>::all(42.42f);
const auto val {42.42f};
// REQUIRE(m44_2(0, 0) == 42.42f);
// REQUIRE(m44_2(0, 1) == 42.42f);
// REQUIRE(m44_2(1, 0) == 42.42f);
// REQUIRE(m44_2(1, 1) == 42.42f);
auto mat_2 = pw::matrix<float,2,2>::all(val);
// m44 *= 2;
// REQUIRE(m44(0, 0) == 84.84f);
// REQUIRE(m44(0, 1) == 84.84f);
// REQUIRE(m44(1, 0) == 84.84f);
// REQUIRE(m44(1, 1) == 84.84f);
REQUIRE(mat_2[0][0] == val);
REQUIRE(mat_2[0][1] == val);
REQUIRE(mat_2[1][0] == val);
REQUIRE(mat_2[1][1] == val);
// m44.set_identity();
// REQUIRE(m44(0, 0) == 1.0f);
// REQUIRE(m44(0, 1) == 0.0f);
// REQUIRE(m44(1, 0) == 0.0f);
// REQUIRE(m44(1, 1) == 1.0f);
mat_2 *= 2;
REQUIRE(mat_2[0][0] == (val * 2));
REQUIRE(mat_2[0][1] == (val * 2));
REQUIRE(mat_2[1][0] == (val * 2));
REQUIRE(mat_2[1][1] == (val * 2));
mat_2 = pw::matrix<float, 2, 2>::identity();
REQUIRE(mat_2[0][0] == 1.0f);
REQUIRE(mat_2[0][1] == 0.0f);
REQUIRE(mat_2[1][0] == 0.0f);
REQUIRE(mat_2[1][1] == 1.0f);
auto mat_2_inv = mat_2.inverse();
REQUIRE(mat_2_inv[0][0] == 1.0f);
REQUIRE(mat_2_inv[0][1] == 0.0f);
REQUIRE(mat_2_inv[1][0] == 0.0f);
REQUIRE(mat_2_inv[1][1] == 1.0f);
// auto m44_2 = pw::matrix2x2::make(0, 1, 2, 3);
@ -60,7 +73,14 @@ TEST_CASE("core", "[matrix]") {
TEST_CASE("core", "[vector.matrix]") {
// auto vec4_1 = pw::vector4_<float>{};
auto vec4_1 = pw::vector4<float>{};
REQUIRE(vec4_1[0] == 0.0f);
REQUIRE(vec4_1[1] == 0.0f);
REQUIRE(vec4_1[2] == 0.0f);
REQUIRE(vec4_1[3] == 0.0f);
// auto mat4_1 = pw::matrix_<4,1,float>{};