Support dynamic size in math::vector
This commit is contained in:
parent
6fc476f1f0
commit
adcf761243
4 changed files with 149 additions and 35 deletions
|
|
@ -1,7 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/math/dynamic.hpp>
|
||||
#include <psemek/util/exception.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::math::detail
|
||||
{
|
||||
|
||||
|
|
@ -31,6 +34,12 @@ namespace psemek::math::detail
|
|||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct array<T, dynamic>
|
||||
{
|
||||
using type = std::vector<T>;
|
||||
};
|
||||
|
||||
template <typename T, typename ... Args>
|
||||
struct all_convertible_to;
|
||||
|
||||
|
|
|
|||
27
libs/math/include/psemek/math/dynamic.hpp
Normal file
27
libs/math/include/psemek/math/dynamic.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/exception.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
|
||||
namespace psemek::math
|
||||
{
|
||||
|
||||
constexpr std::size_t dynamic = static_cast<std::size_t>(-1);
|
||||
|
||||
struct dynamic_size_mismatch
|
||||
: util::exception
|
||||
{
|
||||
dynamic_size_mismatch(std::size_t size1, std::size_t size2, util::stacktrace stacktrace = {})
|
||||
: util::exception(std::format("Dynamic array size mismatch: {} != {}", size1, size2), std::move(stacktrace))
|
||||
{}
|
||||
};
|
||||
|
||||
inline void check_dynamic_size(std::size_t size1, std::size_t size2)
|
||||
{
|
||||
if (size1 != size2)
|
||||
throw dynamic_size_mismatch(size1, size2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include <psemek/math/detail/array.hpp>
|
||||
#include <psemek/util/hash.hpp>
|
||||
#include <psemek/util/assert.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
#include <compare>
|
||||
|
||||
|
|
@ -24,15 +24,31 @@ namespace psemek::math
|
|||
|
||||
vector() = default;
|
||||
|
||||
explicit vector(std::size_t size) requires (N != dynamic)
|
||||
{
|
||||
assert(size == N);
|
||||
}
|
||||
|
||||
explicit vector(std::size_t size) requires (N == dynamic)
|
||||
: coords(size, T{})
|
||||
{}
|
||||
|
||||
template <typename ... Args>
|
||||
requires((sizeof...(Args) == N) && detail::all_convertible_to<T, Args...>::value)
|
||||
requires((sizeof...(Args) == N || N == dynamic) && detail::all_convertible_to<T, Args...>::value)
|
||||
vector(Args && ... args)
|
||||
: coords{ static_cast<T>(std::forward<Args>(args))... }
|
||||
{}
|
||||
|
||||
std::size_t dimension() const
|
||||
{
|
||||
return N;
|
||||
if constexpr (N == dynamic)
|
||||
{
|
||||
return coords.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return N;
|
||||
}
|
||||
}
|
||||
|
||||
T & operator[](std::size_t i)
|
||||
|
|
@ -51,7 +67,8 @@ namespace psemek::math
|
|||
vector & operator += (vector const & v);
|
||||
vector & operator -= (vector const & v);
|
||||
|
||||
static vector zero();
|
||||
static vector zero() requires (N != dynamic);
|
||||
static vector zero(std::size_t size);
|
||||
};
|
||||
|
||||
template <typename ... Args>
|
||||
|
|
@ -60,8 +77,8 @@ namespace psemek::math
|
|||
template <typename T1, typename T, std::size_t N>
|
||||
vector<T1, N> cast(vector<T, N> const & v)
|
||||
{
|
||||
vector<T1, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
vector<T1, N> r(v.dimension());
|
||||
for (std::size_t i = 0; i < v.dimension(); ++i)
|
||||
r[i] = static_cast<T1>(v[i]);
|
||||
return r;
|
||||
}
|
||||
|
|
@ -69,7 +86,9 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
std::strong_ordering operator <=> (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
check_dynamic_size(v1.dimension(), v2.dimension());
|
||||
|
||||
for (std::size_t i = 0; i < v1.dimension(); ++i)
|
||||
{
|
||||
if (v1[i] < v2[i])
|
||||
return std::strong_ordering::less;
|
||||
|
|
@ -118,8 +137,8 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator * (vector<T, N> const & v, T const & s)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
vector<T, N> r(v.dimension());
|
||||
for (std::size_t i = 0; i < v.dimension(); ++i)
|
||||
r[i] = v[i] * s;
|
||||
return r;
|
||||
}
|
||||
|
|
@ -127,8 +146,8 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator * (T const & s, vector<T, N> const & v)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
vector<T, N> r(v.dimension());
|
||||
for (std::size_t i = 0; i < v.dimension(); ++i)
|
||||
r[i] = s * v[i];
|
||||
return r;
|
||||
}
|
||||
|
|
@ -136,8 +155,8 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator / (vector<T, N> const & v, T const & s)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
vector<T, N> r(v.dimension());
|
||||
for (std::size_t i = 0; i < v.dimension(); ++i)
|
||||
r[i] = v[i] / s;
|
||||
return r;
|
||||
}
|
||||
|
|
@ -145,8 +164,8 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator - (vector<T, N> const & v)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
vector<T, N> r(v.dimension());
|
||||
for (std::size_t i = 0; i < v.dimension(); ++i)
|
||||
r[i] = -v[i];
|
||||
return r;
|
||||
}
|
||||
|
|
@ -154,8 +173,10 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator + (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
check_dynamic_size(v1.dimension(), v2.dimension());
|
||||
|
||||
vector<T, N> r(v1.dimension());
|
||||
for (std::size_t i = 0; i < v1.dimension(); ++i)
|
||||
r[i] = v1[i] + v2[i];
|
||||
return r;
|
||||
}
|
||||
|
|
@ -163,8 +184,10 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
vector<T, N> operator - (vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
check_dynamic_size(v1.dimension(), v2.dimension());
|
||||
|
||||
vector<T, N> r(v1.dimension());
|
||||
for (std::size_t i = 0; i < v1.dimension(); ++i)
|
||||
r[i] = v1[i] - v2[i];
|
||||
return r;
|
||||
}
|
||||
|
|
@ -194,7 +217,7 @@ namespace psemek::math
|
|||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> vector<T, N>::zero()
|
||||
vector<T, N> vector<T, N>::zero() requires (N != dynamic)
|
||||
{
|
||||
vector<T, N> r;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
|
|
@ -202,11 +225,27 @@ namespace psemek::math
|
|||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
vector<T, N> vector<T, N>::zero(std::size_t size)
|
||||
{
|
||||
if constexpr (N != dynamic)
|
||||
{
|
||||
assert(N == size);
|
||||
}
|
||||
|
||||
vector<T, N> r(size);
|
||||
for (std::size_t i = 0; i < size; ++i)
|
||||
r[i] = T(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
T dot(vector<T, N> const & v1, vector<T, N> const & v2)
|
||||
{
|
||||
check_dynamic_size(v1.dimension(), v2.dimension());
|
||||
|
||||
T r{};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
for (std::size_t i = 0; i < v1.dimension(); ++i)
|
||||
r += v1[i] * v2[i];
|
||||
return r;
|
||||
}
|
||||
|
|
@ -223,7 +262,7 @@ namespace psemek::math
|
|||
using std::abs;
|
||||
using std::max;
|
||||
T r = abs(v[0]);
|
||||
for (std::size_t i = 1; i < N; ++i)
|
||||
for (std::size_t i = 1; i < v.dimension(); ++i)
|
||||
r = max(r, abs(v[i]));
|
||||
return r;
|
||||
}
|
||||
|
|
@ -279,10 +318,10 @@ namespace psemek::math
|
|||
template <typename T, std::size_t N>
|
||||
vector<T, N> ort(vector<T, N> const & v)
|
||||
{
|
||||
vector<T, N> result;
|
||||
vector<T, N> result = vector<T, N>::zero(v.dimension());
|
||||
if constexpr ((N % 2) == 0)
|
||||
{
|
||||
for (std::size_t i = 0; i < N; i += 2)
|
||||
for (std::size_t i = 0; i < v.dimension(); i += 2)
|
||||
{
|
||||
result[i] = -v[i + 1];
|
||||
result[i + 1] = v[i];
|
||||
|
|
@ -294,7 +333,7 @@ namespace psemek::math
|
|||
|
||||
using std::abs;
|
||||
|
||||
for (std::size_t k = 1; k < N; ++k)
|
||||
for (std::size_t k = 1; k < v.dimension(); ++k)
|
||||
{
|
||||
if (abs(v[k]) > abs(v[i]))
|
||||
{
|
||||
|
|
@ -303,7 +342,6 @@ namespace psemek::math
|
|||
}
|
||||
}
|
||||
|
||||
result = vector<T, N>::zero();
|
||||
result[i] = -v[j];
|
||||
result[j] = v[i];
|
||||
}
|
||||
|
|
@ -357,14 +395,16 @@ namespace psemek::math
|
|||
return {v[0] * c - v[1] * s, v[0] * s + v[1] * c};
|
||||
}
|
||||
|
||||
template <typename F, std::size_t N, typename ... Ts>
|
||||
auto pointwise(F && f, vector<Ts, N> const & ... vs)
|
||||
template <typename F, std::size_t N, typename T, typename ... Ts>
|
||||
auto pointwise(F && f, vector<T, N> const & v0, vector<Ts, N> const & ... vs)
|
||||
{
|
||||
using R = decltype(f(vs[0]...));
|
||||
using R = decltype(f(v0[0], vs[0]...));
|
||||
|
||||
vector<R, N> result;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
result[i] = f(vs[i]...);
|
||||
(check_dynamic_size(v0.dimension(), vs.dimension()), ...);
|
||||
|
||||
vector<R, N> result(v0.dimension());
|
||||
for (std::size_t i = 0; i < v0.dimension(); ++i)
|
||||
result[i] = f(v0[i], vs[i]...);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +488,7 @@ namespace psemek::math
|
|||
}
|
||||
|
||||
os << '(' << v[0];
|
||||
for (std::size_t i = 1; i < N; ++i)
|
||||
for (std::size_t i = 1; i < v.dimension(); ++i)
|
||||
os << ", " << v[i];
|
||||
os << ')';
|
||||
return os;
|
||||
|
|
@ -458,7 +498,7 @@ namespace psemek::math
|
|||
bool isfinite(vector<T, N> const & v)
|
||||
{
|
||||
using std::isfinite;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
for (std::size_t i = 0; i < v.dimension(); ++i)
|
||||
if (!isfinite(v[i]))
|
||||
return false;
|
||||
return true;
|
||||
|
|
@ -476,7 +516,7 @@ namespace std
|
|||
{
|
||||
std::hash<T> h;
|
||||
std::uint64_t r = 0;
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
for (std::size_t i = 0; i < v.dimension(); ++i)
|
||||
::psemek::util::hash_combine(r, h(v[i]));
|
||||
return r;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,24 @@ test_case(math_vector_init)
|
|||
expect_equal(pf[2], 5.f);
|
||||
}
|
||||
|
||||
test_case(math_vector_dynamic)
|
||||
{
|
||||
vector<int, dynamic> pi1(std::size_t(15));
|
||||
vector<int, dynamic> pi2{1, 2, 3, 4, 5};
|
||||
|
||||
static_assert(std::is_same_v<decltype(pi1)::scalar_type, int>);
|
||||
static_assert(std::is_same_v<decltype(pi2)::scalar_type, int>);
|
||||
|
||||
expect_equal(pi1.dimension(), 15);
|
||||
expect_equal(pi2.dimension(), 5);
|
||||
|
||||
expect_equal(pi2[0], 1);
|
||||
expect_equal(pi2[1], 2);
|
||||
expect_equal(pi2[2], 3);
|
||||
expect_equal(pi2[3], 4);
|
||||
expect_equal(pi2[4], 5);
|
||||
}
|
||||
|
||||
test_case(math_vector_deduce)
|
||||
{
|
||||
vector pi{1, 2};
|
||||
|
|
@ -70,6 +88,16 @@ test_case(math_vector_arithmetic)
|
|||
expect_equal(vf1 + vf2, (vector{13.f, 15.f, 17.f}));
|
||||
expect_equal(vf1 - vf2, (vector{-3.f, -3.f, -3.f}));
|
||||
expect_equal(2.f * vf1 - 3.f * vf2, (vector{-14.f, -15.f, -16.f}));
|
||||
|
||||
vector<float, dynamic> vd1{-1.f, -2.f, -3.f};
|
||||
vector<float, dynamic> vd2{-4.f, -5.f, -6.f};
|
||||
|
||||
expect_equal(-vd1, (vector<float, dynamic>{1.f, 2.f, 3.f}));
|
||||
expect_equal(5.f * vd1, (vector<float, dynamic>{-5.f, -10.f, -15.f}));
|
||||
expect_equal(vd1 * 5.f, (vector<float, dynamic>{-5.f, -10.f, -15.f}));
|
||||
expect_equal(vd1 + vd2, (vector<float, dynamic>{-5.f, -7.f, -9.f}));
|
||||
expect_equal(vd1 - vd2, (vector<float, dynamic>{3.f, 3.f, 3.f}));
|
||||
expect_equal(2.f * vd1 - 3.f * vd2, (vector<float, dynamic>{10.f, 11.f, 12.f}));
|
||||
}
|
||||
|
||||
test_case(math_vector_dot)
|
||||
|
|
@ -83,6 +111,11 @@ test_case(math_vector_dot)
|
|||
vector vf2{8.f, 9.f, 10.f};
|
||||
|
||||
expect_equal((dot(vf1, vf2)), 164.f);
|
||||
|
||||
vector<float, dynamic> vd1{-1.f, -2.f, -3.f};
|
||||
vector<float, dynamic> vd2{-4.f, -5.f, -6.f};
|
||||
|
||||
expect_equal((dot(vd1, vd2)), 32.f);
|
||||
}
|
||||
|
||||
test_case(math_vector_det)
|
||||
|
|
@ -118,18 +151,22 @@ test_case(math_vector_cast)
|
|||
{
|
||||
vector pi{1, 2};
|
||||
vector pf{3.f, 4.f, 5.f};
|
||||
vector<int, dynamic> pd{-1.f, -2.f, -3.f};
|
||||
|
||||
expect_equal(cast<float>(pi), (vector{1.f, 2.f}));
|
||||
expect_equal(cast<int>(pf), (vector{3, 4, 5}));
|
||||
expect_equal(cast<int>(pd), (vector<int, dynamic>{-1, -2, -3}));
|
||||
}
|
||||
|
||||
test_case(math_vector_ort)
|
||||
{
|
||||
vector v1{1.f, 2.f};
|
||||
vector v2{3.f, 4.f, 5.f};
|
||||
vector<int, dynamic> v3{-1.f, -2.f, -3.f};
|
||||
|
||||
expect_small((dot(v1, ort(v1))), 1e-6);
|
||||
expect_small((dot(v2, ort(v2))), 1e-6);
|
||||
expect_small((dot(v3, ort(v3))), 1e-6);
|
||||
}
|
||||
|
||||
test_case(math_vector_cross)
|
||||
|
|
@ -169,6 +206,7 @@ test_case(math_vector_slerp)
|
|||
// 10-degree steps
|
||||
auto s = slerp(v1, v2, i / 9.f);
|
||||
expect_close((length(s)), 1.f, 1e-6);
|
||||
expect_close((angle(s)), rad(10.f * i), 1e-6);
|
||||
expect_gequal((det(v1, s)), 0.f);
|
||||
expect_gequal((det(s, v2)), 0.f);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue