Change math::matrix storage type in preparation for dynamic-sized matrices

This commit is contained in:
Nikita Lisitsa 2025-10-25 16:12:38 +03:00
parent 17d857ecf2
commit 774620c673
6 changed files with 331 additions and 52 deletions

View file

@ -93,14 +93,12 @@ namespace psemek::gfx
static auto pointer(math::matrix<T, R, C> & value) static auto pointer(math::matrix<T, R, C> & value)
{ {
return &value[0][0]; return value.values().begin();
} }
static void finalize(math::matrix<T, R, C> & value) static void finalize(math::matrix<T, R, C> & value)
{ {
math::matrix<T, C, R> temp; value = math::transpose(value);
std::copy(value.coords, value.coords + R * C, temp.coords);
value = math::transpose(temp);
} }
}; };

View file

@ -153,47 +153,47 @@ namespace psemek::gfx
void program::uniform_proxy::operator = (math::matrix<float, 2, 2> const & m) void program::uniform_proxy::operator = (math::matrix<float, 2, 2> const & m)
{ {
gl::UniformMatrix2fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix2fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 2, 3> const & m) void program::uniform_proxy::operator = (math::matrix<float, 2, 3> const & m)
{ {
gl::UniformMatrix3x2fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix3x2fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 2, 4> const & m) void program::uniform_proxy::operator = (math::matrix<float, 2, 4> const & m)
{ {
gl::UniformMatrix4x2fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix4x2fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 3, 2> const & m) void program::uniform_proxy::operator = (math::matrix<float, 3, 2> const & m)
{ {
gl::UniformMatrix2x3fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix2x3fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 3, 3> const & m) void program::uniform_proxy::operator = (math::matrix<float, 3, 3> const & m)
{ {
gl::UniformMatrix3fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix3fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 3, 4> const & m) void program::uniform_proxy::operator = (math::matrix<float, 3, 4> const & m)
{ {
gl::UniformMatrix4x3fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix4x3fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 4, 2> const & m) void program::uniform_proxy::operator = (math::matrix<float, 4, 2> const & m)
{ {
gl::UniformMatrix2x4fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix2x4fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 4, 3> const & m) void program::uniform_proxy::operator = (math::matrix<float, 4, 3> const & m)
{ {
gl::UniformMatrix3x4fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix3x4fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::matrix<float, 4, 4> const & m) void program::uniform_proxy::operator = (math::matrix<float, 4, 4> const & m)
{ {
gl::UniformMatrix4fv(location_, 1, gl::TRUE, m.coords); gl::UniformMatrix4fv(location_, 1, gl::TRUE, &m[0][0]);
} }
void program::uniform_proxy::operator = (math::interval<int> const & i) void program::uniform_proxy::operator = (math::interval<int> const & i)

View file

@ -11,7 +11,7 @@ namespace psemek::math::detail
: util::exception : util::exception
{ {
empty_array_exception(util::stacktrace stacktrace = {}) empty_array_exception(util::stacktrace stacktrace = {})
: util::exception("Indexing an empty array", std::move(stacktrace)) : util::exception("Indexing into a zero-dimensional array", std::move(stacktrace))
{} {}
}; };
@ -29,7 +29,7 @@ namespace psemek::math::detail
T const & operator[](std::size_t) const { throw empty_array_exception{}; } T const & operator[](std::size_t) const { throw empty_array_exception{}; }
T & operator[](std::size_t) { throw empty_array_exception{}; } T & operator[](std::size_t) { throw empty_array_exception{}; }
type operator + (std::size_t) const { return *this; } // type operator + (std::size_t) const { return *this; }
}; };
}; };

View file

@ -0,0 +1,218 @@
#pragma once
#include <psemek/math/detail/array.hpp>
namespace psemek::math::detail
{
template <typename T, std::size_t R, std::size_t C>
struct array_2d
{
using type = T[R][C];
};
template <typename T, std::size_t R>
struct array_2d<T, R, 0>
{
struct type
{
static constexpr std::size_t rows = R;
static constexpr std::size_t columns = 0;
T const * operator[](std::size_t) const { throw empty_array_exception{}; }
T * operator[](std::size_t) { throw empty_array_exception{}; }
};
};
template <typename T>
struct array_2d<T, dynamic, 0>
{
struct type
{
std::size_t rows;
static constexpr std::size_t columns = 0;
type(std::size_t rows)
: rows(rows)
{}
T const * operator[](std::size_t) const { throw empty_array_exception{}; }
T * operator[](std::size_t) { throw empty_array_exception{}; }
};
};
template <typename T, std::size_t C>
struct array_2d<T, 0, C>
{
struct type
{
static constexpr std::size_t rows = 0;
static constexpr std::size_t columns = C;
T const * operator[](std::size_t) const { throw empty_array_exception{}; }
T * operator[](std::size_t) { throw empty_array_exception{}; }
};
};
template <typename T>
struct array_2d<T, 0, dynamic>
{
struct type
{
static constexpr std::size_t rows = 0;
std::size_t columns;
type(std::size_t columns)
: columns(columns)
{}
T const * operator[](std::size_t) const { throw empty_array_exception{}; }
T * operator[](std::size_t) { throw empty_array_exception{}; }
};
};
template <typename T>
struct array_2d<T, 0, 0>
{
struct type
{
static constexpr std::size_t rows = 0;
static constexpr std::size_t columns = 0;
T const * operator[](std::size_t) const { throw empty_array_exception{}; }
T * operator[](std::size_t) { throw empty_array_exception{}; }
};
};
template <typename T, std::size_t R>
struct array_2d<T, R, dynamic>
{
struct type
{
static constexpr std::size_t rows = R;
std::size_t columns;
std::unique_ptr<T[]> data;
type()
: columns(0)
{}
type(std::size_t columns)
: columns(columns)
, data(std::make_unique_for_overwrite<T[]>(rows * columns))
{}
type([[maybe_unused]] std::size_t rows, std::size_t columns)
: type(columns)
{
assert(rows == R);
}
T * operator[] (std::size_t row)
{
return data.get() + row * columns;
}
T const * operator[] (std::size_t row) const
{
return data.get() + row * columns;
}
type copy() const
{
type result;
result.columns = columns;
result.data = std::make_unique_for_overwrite<T[]>(rows * columns);
std::copy(data.get(), data.get() + rows * columns, result.data.get());
return result;
}
};
};
template <typename T, std::size_t C>
struct array_2d<T, dynamic, C>
{
struct type
{
std::size_t rows;
static constexpr std::size_t columns = C;
std::unique_ptr<T[]> data;
type()
: rows(0)
{}
type(std::size_t rows)
: rows(rows)
, data(std::make_unique_for_overwrite<T[]>(rows * columns))
{}
type(std::size_t rows, [[maybe_unused]] std::size_t columns)
: type(rows)
{
assert(columns == C);
}
T * operator[] (std::size_t row)
{
return data.get() + row * columns;
}
T const * operator[] (std::size_t row) const
{
return data.get() + row * columns;
}
type copy() const
{
type result;
result.rows = rows;
result.data = std::make_unique_for_overwrite<T[]>(rows * columns);
std::copy(data.get(), data.get() + rows * columns, result.data.get());
return result;
}
};
};
template <typename T>
struct array_2d<T, dynamic, dynamic>
{
struct type
{
std::size_t rows;
std::size_t columns;
std::unique_ptr<T[]> data;
type()
: rows(0)
, columns(0)
{}
type(std::size_t rows, std::size_t columns)
: rows(rows)
, columns(columns)
, data(std::make_unique_for_overwrite<T[]>(rows * columns))
{}
T * operator[] (std::size_t row)
{
return data.get() + row * columns;
}
T const * operator[] (std::size_t row) const
{
return data.get() + row * columns;
}
type copy() const
{
type result;
result.rows = rows;
result.data = std::make_unique_for_overwrite<T[]>(rows * columns);
std::copy(data.get(), data.get() + rows * columns, result.data.get());
return result;
}
};
};
}

View file

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <psemek/math/detail/array.hpp> #include <psemek/math/detail/array_2d.hpp>
#include <psemek/math/vector.hpp> #include <psemek/math/vector.hpp>
#include <psemek/math/math.hpp> #include <psemek/math/math.hpp>
#include <psemek/util/range.hpp>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -18,26 +19,64 @@ namespace psemek::math
static constexpr std::size_t static_rows = R; static constexpr std::size_t static_rows = R;
static constexpr std::size_t static_columns = C; static constexpr std::size_t static_columns = C;
typename detail::array<T, R * C>::type coords; typename detail::array_2d<T, R, C>::type coords;
std::size_t rows() const std::size_t rows() const
{ {
return R; if constexpr (R == dynamic)
{
return coords.rows;
}
else
{
return R;
}
} }
std::size_t columns() const std::size_t columns() const
{ {
return C; if constexpr (C == dynamic)
{
return coords.columns;
}
else
{
return C;
}
} }
auto operator[](std::size_t i) auto operator[](std::size_t i)
{ {
return coords + C * i; return coords[i];
} }
auto operator[](std::size_t i) const auto operator[](std::size_t i) const
{ {
return coords + C * i; return coords[i];
}
util::range<T *> values()
{
return {&coords[0][0], &coords[0][0] + rows() * columns()};
}
util::range<T const *> values() const
{
return {&coords[0][0], &coords[0][0] + rows() * columns()};
}
matrix copy() const
{
if constexpr (R == dynamic || C == dynamic)
{
matrix r;
r.coords = coords.copy();
return r;
}
else
{
return *this;
}
} }
matrix & operator *= (T const & s); matrix & operator *= (T const & s);
@ -56,8 +95,8 @@ namespace psemek::math
matrix<T, R, C> matrix<T, R, C>::zero() matrix<T, R, C> matrix<T, R, C>::zero()
{ {
matrix<T, R, C> m; matrix<T, R, C> m;
for (std::size_t i = 0; i < R * C; ++i) for (auto & v : m.values())
m.coords[i] = T(0); v = T(0);
return m; return m;
} }
@ -89,20 +128,24 @@ namespace psemek::math
matrix<T1, R, C> cast(matrix<T, R, C> const & m) matrix<T1, R, C> cast(matrix<T, R, C> const & m)
{ {
matrix<T1, R, C> r; matrix<T1, R, C> r;
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
r.coords[i] = static_cast<T1>(m.coords[i]); for (std::size_t j = 0; j < C; ++j)
r[i][j] = static_cast<T1>(m[i][j]);
return r; return r;
} }
template <typename T, std::size_t R, std::size_t C> template <typename T, std::size_t R, std::size_t C>
std::strong_ordering operator <=> (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2) std::strong_ordering operator <=> (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
{ {
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
{ {
if (m1.coords[i] < m2.coords[i]) for (std::size_t j = 0; j < C; ++j)
return std::strong_ordering::less; {
else if (m1.coords[i] > m2.coords[i]) if (m1[i][j] < m2[i][j])
return std::strong_ordering::greater; return std::strong_ordering::less;
else if (m1[i][j] > m2[i][j])
return std::strong_ordering::greater;
}
} }
return std::strong_ordering::equal; return std::strong_ordering::equal;
} }
@ -147,8 +190,9 @@ namespace psemek::math
matrix<T, R, C> operator * (matrix<T, R, C> const & m, T const & s) matrix<T, R, C> operator * (matrix<T, R, C> const & m, T const & s)
{ {
matrix<T, R, C> r; matrix<T, R, C> r;
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
r.coords[i] = m.coords[i] * s; for (std::size_t j = 0; j < C; ++j)
r[i][j] = m[i][j] * s;
return r; return r;
} }
@ -156,8 +200,9 @@ namespace psemek::math
matrix<T, R, C> operator * (T const & s, matrix<T, R, C> const & m) matrix<T, R, C> operator * (T const & s, matrix<T, R, C> const & m)
{ {
matrix<T, R, C> r; matrix<T, R, C> r;
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
r.coords[i] = s * m.coords[i]; for (std::size_t j = 0; j < C; ++j)
r[i][j] = m[i][j] * s;
return r; return r;
} }
@ -165,8 +210,9 @@ namespace psemek::math
matrix<T, R, C> operator / (matrix<T, R, C> const & m, T const & s) matrix<T, R, C> operator / (matrix<T, R, C> const & m, T const & s)
{ {
matrix<T, R, C> r; matrix<T, R, C> r;
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
r.coords[i] = m.coords[i] / s; for (std::size_t j = 0; j < C; ++j)
r[i][j] = m[i][j] / s;
return r; return r;
} }
@ -188,8 +234,9 @@ namespace psemek::math
matrix<T, R, C> operator - (matrix<T, R, C> const & m) matrix<T, R, C> operator - (matrix<T, R, C> const & m)
{ {
matrix<T, R, C> r; matrix<T, R, C> r;
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
r.coords[i] = -m.coords[i]; for (std::size_t j = 0; j < C; ++j)
r[i][j] = -m[i][j];
return r; return r;
} }
@ -197,8 +244,9 @@ namespace psemek::math
matrix<T, R, C> operator + (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2) matrix<T, R, C> operator + (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
{ {
matrix<T, R, C> r; matrix<T, R, C> r;
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
r.coords[i] = m1.coords[i] + m2.coords[i]; for (std::size_t j = 0; j < C; ++j)
r[i][j] = m1[i][j] + m2[i][j];
return r; return r;
} }
@ -206,8 +254,9 @@ namespace psemek::math
matrix<T, R, C> operator - (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2) matrix<T, R, C> operator - (matrix<T, R, C> const & m1, matrix<T, R, C> const & m2)
{ {
matrix<T, R, C> r; matrix<T, R, C> r;
for (std::size_t i = 0; i < R * C; ++i) for (std::size_t i = 0; i < R; ++i)
r.coords[i] = m1.coords[i] - m2.coords[i]; for (std::size_t j = 0; j < C; ++j)
r[i][j] = m1[i][j] - m2[i][j];
return r; return r;
} }
@ -283,11 +332,11 @@ namespace psemek::math
{ {
vector<T, N> m[] = {v, rows...}; vector<T, N> m[] = {v, rows...};
matrix<T, sizeof...(Rows) + 1, N> result; matrix<T, sizeof...(Rows) + 1, N> r;
for (std::size_t i = 0; i < result.rows(); ++i) for (std::size_t i = 0; i < r.rows(); ++i)
for (std::size_t j = 0; j < result.columns(); ++j) for (std::size_t j = 0; j < r.columns(); ++j)
result[i][j] = m[i][j]; r[i][j] = m[i][j];
return result; return r;
} }
template <typename T, std::size_t N, typename ... Columns> template <typename T, std::size_t N, typename ... Columns>
@ -295,11 +344,11 @@ namespace psemek::math
{ {
vector<T, N> m[] = {v, columns...}; vector<T, N> m[] = {v, columns...};
matrix<T, N, sizeof...(Columns) + 1> result; matrix<T, N, sizeof...(Columns) + 1> r;
for (std::size_t i = 0; i < result.rows(); ++i) for (std::size_t i = 0; i < r.rows(); ++i)
for (std::size_t j = 0; j < result.columns(); ++j) for (std::size_t j = 0; j < r.columns(); ++j)
result[i][j] = m[j][i]; r[i][j] = m[j][i];
return result; return r;
} }
template <typename T, std::size_t R, std::size_t C> template <typename T, std::size_t R, std::size_t C>

View file

@ -51,6 +51,20 @@ namespace psemek::math
} }
} }
vector copy() const
{
if constexpr (N == dynamic)
{
vector result;
result.coords = coords.copy();
return result;
}
else
{
return *this;
}
}
T & operator[](std::size_t i) T & operator[](std::size_t i)
{ {
return coords[i]; return coords[i];