Compare commits
No commits in common. "582b7c09c6bdd8d2d59b1f33b6bb193731e72c69" and "3e2b94ec588edb01af1ca7a6bf67e67bc9cb295e" have entirely different histories.
582b7c09c6
...
3e2b94ec58
21 changed files with 200 additions and 1347 deletions
|
|
@ -41,11 +41,7 @@ add_module(
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
preliminary
|
preliminary
|
||||||
TESTS
|
TESTS
|
||||||
trig.test.cpp
|
|
||||||
vec2.test.cpp
|
vec2.test.cpp
|
||||||
vec3.test.cpp
|
|
||||||
vec4.test.cpp
|
|
||||||
mat4.test.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_module(
|
add_module(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export module logger;
|
||||||
|
|
||||||
import preliminary;
|
import preliminary;
|
||||||
|
|
||||||
export namespace lt::log {
|
namespace lt::log {
|
||||||
|
|
||||||
/** Severity of a log message. */
|
/** Severity of a log message. */
|
||||||
enum class Level : u8
|
enum class Level : u8
|
||||||
|
|
@ -25,25 +25,19 @@ enum class Level : u8
|
||||||
/** Unrecoverable errors */
|
/** Unrecoverable errors */
|
||||||
critical = 5,
|
critical = 5,
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs from the testing-framework.
|
|
||||||
* Highest so we still get them while turning off all logs from the code under test.
|
|
||||||
*
|
|
||||||
* @note: log::test does NOT include source_location
|
|
||||||
*/
|
|
||||||
test = 6,
|
|
||||||
|
|
||||||
/** No logging */
|
/** No logging */
|
||||||
off = 7,
|
off = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto min_severity = Level::trace;
|
namespace details {
|
||||||
|
|
||||||
auto set_min_severity(Level severity)
|
inline auto thread_hash_id() noexcept -> u64
|
||||||
{
|
{
|
||||||
min_severity = severity;
|
return static_cast<u64>(std::hash<std::thread::id> {}(std::this_thread::get_id()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
struct [[maybe_unused]] print
|
struct [[maybe_unused]] print
|
||||||
{
|
{
|
||||||
|
|
@ -54,11 +48,6 @@ struct [[maybe_unused]] print
|
||||||
Args &&...arguments
|
Args &&...arguments
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
if (std::to_underlying(level) < std::to_underlying(min_severity))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr auto to_string = [](Level level) {
|
constexpr auto to_string = [](Level level) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
switch (level)
|
switch (level)
|
||||||
|
|
@ -86,45 +75,13 @@ struct [[maybe_unused]] print
|
||||||
std::format(format, std::forward<Args>(arguments)...)
|
std::format(format, std::forward<Args>(arguments)...)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] print(
|
|
||||||
Level level,
|
|
||||||
std::format_string<Args...> format,
|
|
||||||
Args &&...arguments
|
|
||||||
) noexcept
|
|
||||||
{
|
|
||||||
constexpr auto to_string = [](Level level) {
|
|
||||||
// clang-format off
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
using enum ::lt::log::Level;
|
|
||||||
case trace : return "\033[1;37m| trc |\033[0m";
|
|
||||||
case debug : return "\033[1;36m| dbg |\033[0m";
|
|
||||||
case info : return "\033[1;32m| inf |\033[0m";
|
|
||||||
case warn : return "\033[1;33m| wrn |\033[0m";
|
|
||||||
case error : return "\033[1;31m| err |\033[0m";
|
|
||||||
case critical: return "\033[1;41m| crt |\033[0m";
|
|
||||||
case test : return "\033[1;33m| test |\033[0m";
|
|
||||||
case off : return "";
|
|
||||||
}
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
std::unreachable();
|
|
||||||
};
|
|
||||||
|
|
||||||
std::println(
|
|
||||||
"{} {}",
|
|
||||||
to_string(level),
|
|
||||||
std::format(format, std::forward<Args>(arguments)...)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
print(Level, const std::source_location &, std::format_string<Args...>, Args &&...) noexcept
|
print(Level, const std::source_location &, std::format_string<Args...>, Args &&...) noexcept
|
||||||
-> print<Args...>;
|
-> print<Args...>;
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
struct [[maybe_unused]] trace
|
struct [[maybe_unused]] trace
|
||||||
{
|
{
|
||||||
[[maybe_unused]] trace(
|
[[maybe_unused]] trace(
|
||||||
|
|
@ -137,10 +94,10 @@ struct [[maybe_unused]] trace
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
trace(std::format_string<Args...>, Args &&...) noexcept -> trace<Args...>;
|
trace(std::format_string<Args...>, Args &&...) noexcept -> trace<Args...>;
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
struct [[maybe_unused]] debug
|
struct [[maybe_unused]] debug
|
||||||
{
|
{
|
||||||
[[maybe_unused]] debug(
|
[[maybe_unused]] debug(
|
||||||
|
|
@ -153,11 +110,10 @@ struct [[maybe_unused]] debug
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
debug(std::format_string<Args...>, Args &&...) noexcept -> debug<Args...>;
|
debug(std::format_string<Args...>, Args &&...) noexcept -> debug<Args...>;
|
||||||
|
|
||||||
|
export template<typename... Args>
|
||||||
template<typename... Args>
|
|
||||||
struct [[maybe_unused]] info
|
struct [[maybe_unused]] info
|
||||||
{
|
{
|
||||||
[[maybe_unused]] info(
|
[[maybe_unused]] info(
|
||||||
|
|
@ -170,10 +126,10 @@ struct [[maybe_unused]] info
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
info(std::format_string<Args...>, Args &&...) noexcept -> info<Args...>;
|
info(std::format_string<Args...>, Args &&...) noexcept -> info<Args...>;
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
struct [[maybe_unused]] warn
|
struct [[maybe_unused]] warn
|
||||||
{
|
{
|
||||||
[[maybe_unused]] warn(
|
[[maybe_unused]] warn(
|
||||||
|
|
@ -186,10 +142,10 @@ struct [[maybe_unused]] warn
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
warn(std::format_string<Args...>, Args &&...) noexcept -> warn<Args...>;
|
warn(std::format_string<Args...>, Args &&...) noexcept -> warn<Args...>;
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
struct [[maybe_unused]] error
|
struct [[maybe_unused]] error
|
||||||
{
|
{
|
||||||
[[maybe_unused]] error(
|
[[maybe_unused]] error(
|
||||||
|
|
@ -202,10 +158,10 @@ struct [[maybe_unused]] error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
error(std::format_string<Args...>, Args &&...) noexcept -> error<Args...>;
|
error(std::format_string<Args...>, Args &&...) noexcept -> error<Args...>;
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
struct [[maybe_unused]] critical
|
struct [[maybe_unused]] critical
|
||||||
{
|
{
|
||||||
[[maybe_unused]] critical(
|
[[maybe_unused]] critical(
|
||||||
|
|
@ -218,13 +174,7 @@ struct [[maybe_unused]] critical
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Args>
|
export template<typename... Args>
|
||||||
critical(std::format_string<Args...>, Args &&...) noexcept -> critical<Args...>;
|
critical(std::format_string<Args...>, Args &&...) noexcept -> critical<Args...>;
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void test(std::format_string<Args...> format, Args &&...arguments) noexcept
|
|
||||||
{
|
|
||||||
print(Level::test, format, std::forward<Args>(arguments)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace lt::log
|
} // namespace lt::log
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,12 @@ export namespace lt::math {
|
||||||
*
|
*
|
||||||
* the 1 at [z][3] is to save the Z axis into the resulting W for perspective division.
|
* the 1 at [z][3] is to save the Z axis into the resulting W for perspective division.
|
||||||
*
|
*
|
||||||
* @ref Thanks to @pikuma for explaining the math behind this:
|
* @ref Thanks to pikuma for explaining the math behind this:
|
||||||
* https://www.youtube.com/watch?v=EqNcqBdrNyI
|
* https://www.youtube.com/watch?v=EqNcqBdrNyI
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires(std::is_arithmetic_v<T>)
|
requires(std::is_arithmetic_v<T>)
|
||||||
constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far) -> mat4_impl<T>
|
constexpr auto perspective(T field_of_view, T aspect_ratio, T z_near, T z_far)
|
||||||
{
|
{
|
||||||
const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2));
|
const T half_fov_tan = std::tan(field_of_view / static_cast<T>(2));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,13 @@ import math.vec4;
|
||||||
|
|
||||||
export namespace lt::math {
|
export namespace lt::math {
|
||||||
|
|
||||||
/** A 4 by 4 matrix, column major order
|
|
||||||
*
|
|
||||||
* @todo(Light): Use std::simd when it's implemented. */
|
|
||||||
template<typename T = f32>
|
template<typename T = f32>
|
||||||
requires(std::is_arithmetic_v<T>)
|
requires(std::is_arithmetic_v<T>)
|
||||||
struct mat4_impl
|
struct mat4_impl
|
||||||
{
|
{
|
||||||
using Column_T = vec4_impl<T>;
|
using Column_T = vec4_impl<T>;
|
||||||
|
|
||||||
using Underlying_T = Column_T::Underlying_T;
|
constexpr explicit mat4_impl(T scalar = 0)
|
||||||
|
|
||||||
static constexpr auto num_elements = 4u * 4u;
|
|
||||||
|
|
||||||
constexpr explicit mat4_impl(T scalar = T {})
|
|
||||||
: values(
|
: values(
|
||||||
{
|
{
|
||||||
Column_T { scalar },
|
Column_T { scalar },
|
||||||
|
|
@ -32,12 +25,13 @@ struct mat4_impl
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr mat4_impl(
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const T& x0, const T& x1, const T& x2, const T& x3,
|
constexpr mat4_impl(
|
||||||
const T& y0, const T& y1, const T& y2, const T& y3,
|
const T& x0, const T& y0, const T& z0, const T& w0,
|
||||||
const T& z0, const T& z1, const T& z2, const T& z3,
|
const T& x1, const T& y1, const T& z1, const T& w1,
|
||||||
const T& w0, const T& w1, const T& w2, const T& w3)
|
const T& x2, const T& y2, const T& z2, const T& w2,
|
||||||
|
const T& x3, const T& y3, const T& z3, const T& w3
|
||||||
|
)
|
||||||
// clang-format on
|
// clang-format on
|
||||||
: values({ { x0, x1, x2, x3 }, { y0, y1, y2, y3 }, { z0, z1, z2, z3 }, { w0, w1, w2, w3 } })
|
: values({ { x0, x1, x2, x3 }, { y0, y1, y2, y3 }, { z0, z1, z2, z3 }, { w0, w1, w2, w3 } })
|
||||||
{
|
{
|
||||||
|
|
@ -63,41 +57,8 @@ struct mat4_impl
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator*(const mat4_impl<T> &other) const -> mat4_impl<T>
|
|
||||||
{
|
|
||||||
const auto &[a_x, a_y, a_z, a_w] = values;
|
|
||||||
const auto &[b_x, b_y, b_z, b_w] = other.values;
|
|
||||||
|
|
||||||
return mat4_impl<T>(
|
|
||||||
// X column
|
|
||||||
a_x.x * b_x.x + a_y.x * b_x.y + a_z.x * b_x.z + a_w.x * b_x.w,
|
|
||||||
a_x.y * b_x.x + a_y.y * b_x.y + a_z.y * b_x.z + a_w.y * b_x.w,
|
|
||||||
a_x.z * b_x.x + a_y.z * b_x.y + a_z.z * b_x.z + a_w.z * b_x.w,
|
|
||||||
a_x.w * b_x.x + a_y.w * b_x.y + a_z.w * b_x.z + a_w.w * b_x.w,
|
|
||||||
|
|
||||||
// Y column
|
|
||||||
a_x.x * b_y.x + a_y.x * b_y.y + a_z.x * b_y.z + a_w.x * b_y.w,
|
|
||||||
a_x.y * b_y.x + a_y.y * b_y.y + a_z.y * b_y.z + a_w.y * b_y.w,
|
|
||||||
a_x.z * b_y.x + a_y.z * b_y.y + a_z.z * b_y.z + a_w.z * b_y.w,
|
|
||||||
a_x.w * b_y.x + a_y.w * b_y.y + a_z.w * b_y.z + a_w.w * b_y.w,
|
|
||||||
|
|
||||||
// Z column
|
|
||||||
a_x.x * b_z.x + a_y.x * b_z.y + a_z.x * b_z.z + a_w.x * b_z.w,
|
|
||||||
a_x.y * b_z.x + a_y.y * b_z.y + a_z.y * b_z.z + a_w.y * b_z.w,
|
|
||||||
a_x.z * b_z.x + a_y.z * b_z.y + a_z.z * b_z.z + a_w.z * b_z.w,
|
|
||||||
a_x.w * b_z.x + a_y.w * b_z.y + a_z.w * b_z.z + a_w.w * b_z.w,
|
|
||||||
|
|
||||||
// W column
|
|
||||||
a_x.x * b_w.x + a_y.x * b_w.y + a_z.x * b_w.z + a_w.x * b_w.w,
|
|
||||||
a_x.y * b_w.x + a_y.y * b_w.y + a_z.y * b_w.z + a_w.y * b_w.w,
|
|
||||||
a_x.z * b_w.x + a_y.z * b_w.y + a_z.z * b_w.z + a_w.z * b_w.w,
|
|
||||||
a_x.w * b_w.x + a_y.w * b_w.y + a_z.w * b_w.z + a_w.w * b_w.w
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator[](size_t idx) -> Column_T &
|
[[nodiscard]] constexpr auto operator[](size_t idx) -> Column_T &
|
||||||
{
|
{
|
||||||
debug_check(idx < num_elements, "mat4 out of bound access: {}", idx);
|
|
||||||
return values[idx];
|
return values[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,80 +67,51 @@ struct mat4_impl
|
||||||
return values[idx];
|
return values[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static constexpr auto transpose(const mat4_impl<T> &mat) -> mat4_impl<T>
|
[[nodiscard]] constexpr auto operator*(const mat4_impl<T> &other) const -> mat4_impl<T>
|
||||||
{
|
{
|
||||||
const auto &[x, y, z, w] = mat.values;
|
return mat4_impl<T> {};
|
||||||
return mat4_impl<T> {
|
|
||||||
x.x, y.x, z.x, w.x, x.y, y.y, z.y, w.y, x.z, y.z, z.z, w.z, x.w, y.w, z.w, w.w,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static constexpr auto translate(const vec3_impl<T> &vec) -> mat4_impl<T>
|
[[nodiscard]] constexpr auto operator*(const vec4_impl<T> &other) const -> vec4_impl<T>
|
||||||
{
|
{
|
||||||
return mat4_impl<T>(
|
return vec4_impl<T> {};
|
||||||
T { 1 },
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
|
|
||||||
T { 0 },
|
|
||||||
T { 1 },
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
T { 1 },
|
|
||||||
T { 0 },
|
|
||||||
|
|
||||||
vec.x,
|
|
||||||
vec.y,
|
|
||||||
vec.z,
|
|
||||||
T { 1 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr auto scale(const vec3_impl<T> &vec) -> mat4_impl<T>
|
|
||||||
{
|
|
||||||
return mat4_impl<T>(
|
|
||||||
vec.x,
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
|
|
||||||
T { 0 },
|
|
||||||
vec.y,
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
vec.z,
|
|
||||||
T { 0 },
|
|
||||||
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
T { 0 },
|
|
||||||
T { 1 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<Column_T, 4u> values;
|
std::array<Column_T, 4u> values;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @todo(Light): Implement */
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] auto translate(const vec3_impl<T> &value) -> mat4_impl<T>
|
||||||
|
{
|
||||||
|
return mat4_impl<T> {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @todo(Light): Implement */
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] auto rotate(f32 value, const vec3_impl<T> &xyz) -> mat4_impl<T>
|
||||||
|
{
|
||||||
|
return mat4_impl<T> {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @todo(Light): Implement */
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] auto scale(const vec3_impl<T> &value) -> mat4_impl<T>
|
||||||
|
{
|
||||||
|
return mat4_impl<T> {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @todo(Light): Implement */
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] auto inverse(const mat4_impl<T> &value) -> mat4_impl<T>
|
||||||
|
{
|
||||||
|
return mat4_impl<T> {};
|
||||||
|
}
|
||||||
|
|
||||||
using mat4 = mat4_impl<f32>;
|
using mat4 = mat4_impl<f32>;
|
||||||
|
|
||||||
using mat4_f32 = mat4;
|
using imat4 = mat4_impl<i32>;
|
||||||
using mat4_f64 = mat4_impl<f64>;
|
|
||||||
|
|
||||||
using mat4_i8 = mat4_impl<i8>;
|
using umat4 = mat4_impl<u32>;
|
||||||
using mat4_i16 = mat4_impl<i16>;
|
|
||||||
using mat4_i32 = mat4_impl<i32>;
|
|
||||||
using mat4_i64 = mat4_impl<i64>;
|
|
||||||
|
|
||||||
using mat4_u8 = mat4_impl<u8>;
|
|
||||||
using mat4_u16 = mat4_impl<u16>;
|
|
||||||
using mat4_u32 = mat4_impl<u32>;
|
|
||||||
using mat4_u64 = mat4_impl<u64>;
|
|
||||||
|
|
||||||
} // namespace lt::math
|
} // namespace lt::math
|
||||||
|
|
|
||||||
|
|
@ -1,413 +0,0 @@
|
||||||
import test;
|
|
||||||
import math.vec3;
|
|
||||||
import math.mat4;
|
|
||||||
|
|
||||||
using vec3 = ::lt::math::vec3;
|
|
||||||
using mat4 = ::lt::math::mat4;
|
|
||||||
|
|
||||||
Suite static_tests = "mat4_static_checks"_suite = [] {
|
|
||||||
constexpr auto num_elements = lt::math::mat4::num_elements;
|
|
||||||
|
|
||||||
static_assert(num_elements == 4u * 4u);
|
|
||||||
static_assert(std::is_same_v<lt::math::mat4, lt::math::mat4_f32>);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::mat4_f32) == sizeof(f32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::mat4_f64) == sizeof(f64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::mat4_i8) == sizeof(i8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::mat4_i16) == sizeof(i16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::mat4_i32) == sizeof(i32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::mat4_i64) == sizeof(i64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::mat4_u8) == sizeof(u8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::mat4_u16) == sizeof(u16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::mat4_u32) == sizeof(u32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::mat4_u64) == sizeof(u64) * num_elements);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite raii = "mat4_raii"_suite = [] {
|
|
||||||
Case { "happy paths" } = [] {
|
|
||||||
ignore = mat4 {};
|
|
||||||
ignore = mat4 { 1.0 };
|
|
||||||
ignore = mat4 {
|
|
||||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
|
||||||
};
|
|
||||||
ignore = mat4 {
|
|
||||||
mat4::Column_T { 1.0, 2.0, 3.0, 4.0 },
|
|
||||||
mat4::Column_T { 5.0, 6.0, 7.0, 8.0 },
|
|
||||||
mat4::Column_T { 9.0, 10.0, 11.0, 12.0 },
|
|
||||||
mat4::Column_T { 13.0, 14.0, 15.0, 16.0 },
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "unhappy paths" } = [] {
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "many" } = [] {
|
|
||||||
for (auto idx : std::views::iota(0, 1'000'000))
|
|
||||||
{
|
|
||||||
ignore = idx;
|
|
||||||
ignore = mat4 {};
|
|
||||||
ignore = mat4 { 1.0 };
|
|
||||||
ignore = mat4 {
|
|
||||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
|
|
||||||
9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
|
||||||
};
|
|
||||||
ignore = mat4 {
|
|
||||||
mat4::Column_T { 1.0, 2.0, 3.0, 4.0 },
|
|
||||||
mat4::Column_T { 5.0, 6.0, 7.0, 8.0 },
|
|
||||||
mat4::Column_T { 9.0, 10.0, 11.0, 12.0 },
|
|
||||||
mat4::Column_T { 13.0, 14.0, 15.0, 16.0 },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post default construct has correct state" } = [] {
|
|
||||||
const auto [x, y, z, w] = mat4 {}.values;
|
|
||||||
|
|
||||||
expect_eq(x[0], mat4::Underlying_T {});
|
|
||||||
expect_eq(x[1], mat4::Underlying_T {});
|
|
||||||
expect_eq(x[2], mat4::Underlying_T {});
|
|
||||||
expect_eq(x[3], mat4::Underlying_T {});
|
|
||||||
|
|
||||||
expect_eq(y[0], mat4::Underlying_T {});
|
|
||||||
expect_eq(y[1], mat4::Underlying_T {});
|
|
||||||
expect_eq(y[2], mat4::Underlying_T {});
|
|
||||||
expect_eq(y[3], mat4::Underlying_T {});
|
|
||||||
|
|
||||||
expect_eq(z[0], mat4::Underlying_T {});
|
|
||||||
expect_eq(z[1], mat4::Underlying_T {});
|
|
||||||
expect_eq(z[2], mat4::Underlying_T {});
|
|
||||||
expect_eq(z[3], mat4::Underlying_T {});
|
|
||||||
|
|
||||||
expect_eq(w[0], mat4::Underlying_T {});
|
|
||||||
expect_eq(w[1], mat4::Underlying_T {});
|
|
||||||
expect_eq(w[2], mat4::Underlying_T {});
|
|
||||||
expect_eq(w[3], mat4::Underlying_T {});
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post scalar construct has correct state" } = [] {
|
|
||||||
const auto [x, y, z, w] = mat4 { 69.0 }.values;
|
|
||||||
|
|
||||||
expect_eq(x[0], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(x[1], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(x[2], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(x[3], mat4::Underlying_T { 69.0 });
|
|
||||||
|
|
||||||
expect_eq(y[0], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(y[1], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(y[2], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(y[3], mat4::Underlying_T { 69.0 });
|
|
||||||
|
|
||||||
expect_eq(z[0], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(z[1], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(z[2], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(z[3], mat4::Underlying_T { 69.0 });
|
|
||||||
|
|
||||||
expect_eq(w[0], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(w[1], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(w[2], mat4::Underlying_T { 69.0 });
|
|
||||||
expect_eq(w[3], mat4::Underlying_T { 69.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with all values has correct state" } = [] {
|
|
||||||
const auto [x, y, z, w] = mat4 {
|
|
||||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
|
||||||
}.values;
|
|
||||||
|
|
||||||
expect_eq(x[0], mat4::Underlying_T { 1.0 });
|
|
||||||
expect_eq(x[1], mat4::Underlying_T { 2.0 });
|
|
||||||
expect_eq(x[2], mat4::Underlying_T { 3.0 });
|
|
||||||
expect_eq(x[3], mat4::Underlying_T { 4.0 });
|
|
||||||
|
|
||||||
expect_eq(y[0], mat4::Underlying_T { 5.0 });
|
|
||||||
expect_eq(y[1], mat4::Underlying_T { 6.0 });
|
|
||||||
expect_eq(y[2], mat4::Underlying_T { 7.0 });
|
|
||||||
expect_eq(y[3], mat4::Underlying_T { 8.0 });
|
|
||||||
|
|
||||||
expect_eq(z[0], mat4::Underlying_T { 9.0 });
|
|
||||||
expect_eq(z[1], mat4::Underlying_T { 10.0 });
|
|
||||||
expect_eq(z[2], mat4::Underlying_T { 11.0 });
|
|
||||||
expect_eq(z[3], mat4::Underlying_T { 12.0 });
|
|
||||||
|
|
||||||
expect_eq(w[0], mat4::Underlying_T { 13.0 });
|
|
||||||
expect_eq(w[1], mat4::Underlying_T { 14.0 });
|
|
||||||
expect_eq(w[2], mat4::Underlying_T { 15.0 });
|
|
||||||
expect_eq(w[3], mat4::Underlying_T { 16.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with columns has correct state" } = [] {
|
|
||||||
const auto [x, y, z, w] = mat4 {
|
|
||||||
mat4::Column_T { 1.0, 2.0, 3.0, 4.0 },
|
|
||||||
mat4::Column_T { 5.0, 6.0, 7.0, 8.0 },
|
|
||||||
mat4::Column_T { 9.0, 10.0, 11.0, 12.0 },
|
|
||||||
mat4::Column_T { 13.0, 14.0, 15.0, 16.0 },
|
|
||||||
}.values;
|
|
||||||
|
|
||||||
expect_eq(x[0], mat4::Underlying_T { 1.0 });
|
|
||||||
expect_eq(x[1], mat4::Underlying_T { 2.0 });
|
|
||||||
expect_eq(x[2], mat4::Underlying_T { 3.0 });
|
|
||||||
expect_eq(x[3], mat4::Underlying_T { 4.0 });
|
|
||||||
|
|
||||||
expect_eq(y[0], mat4::Underlying_T { 5.0 });
|
|
||||||
expect_eq(y[1], mat4::Underlying_T { 6.0 });
|
|
||||||
expect_eq(y[2], mat4::Underlying_T { 7.0 });
|
|
||||||
expect_eq(y[3], mat4::Underlying_T { 8.0 });
|
|
||||||
|
|
||||||
expect_eq(z[0], mat4::Underlying_T { 9.0 });
|
|
||||||
expect_eq(z[1], mat4::Underlying_T { 10.0 });
|
|
||||||
expect_eq(z[2], mat4::Underlying_T { 11.0 });
|
|
||||||
expect_eq(z[3], mat4::Underlying_T { 12.0 });
|
|
||||||
|
|
||||||
expect_eq(w[0], mat4::Underlying_T { 13.0 });
|
|
||||||
expect_eq(w[1], mat4::Underlying_T { 14.0 });
|
|
||||||
expect_eq(w[2], mat4::Underlying_T { 15.0 });
|
|
||||||
expect_eq(w[3], mat4::Underlying_T { 16.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct identity matrix has correct state" } = [] {
|
|
||||||
const auto [x, y, z, w] = mat4::identity().values;
|
|
||||||
|
|
||||||
expect_eq(x[0], mat4::Underlying_T { 1 });
|
|
||||||
expect_eq(x[1], mat4::Underlying_T {});
|
|
||||||
expect_eq(x[2], mat4::Underlying_T {});
|
|
||||||
expect_eq(x[3], mat4::Underlying_T {});
|
|
||||||
|
|
||||||
expect_eq(y[0], mat4::Underlying_T {});
|
|
||||||
expect_eq(y[1], mat4::Underlying_T { 1 });
|
|
||||||
expect_eq(y[2], mat4::Underlying_T {});
|
|
||||||
expect_eq(y[3], mat4::Underlying_T {});
|
|
||||||
|
|
||||||
expect_eq(z[0], mat4::Underlying_T {});
|
|
||||||
expect_eq(z[1], mat4::Underlying_T {});
|
|
||||||
expect_eq(z[2], mat4::Underlying_T { 1 });
|
|
||||||
expect_eq(z[3], mat4::Underlying_T {});
|
|
||||||
|
|
||||||
expect_eq(w[0], mat4::Underlying_T {});
|
|
||||||
expect_eq(w[1], mat4::Underlying_T {});
|
|
||||||
expect_eq(w[2], mat4::Underlying_T {});
|
|
||||||
expect_eq(w[3], mat4::Underlying_T { 1 });
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite arithmetic_operators = "mat4_arithmetic_operators"_suite = [] {
|
|
||||||
Case { "operator *" } = [] {
|
|
||||||
const auto lhs = mat4 {
|
|
||||||
mat4::Column_T { 1.0, 2.0, 3.0, 4.0 },
|
|
||||||
mat4::Column_T { 5.0, 6.0, 7.0, 8.0 },
|
|
||||||
mat4::Column_T { 9.0, 10.0, 11.0, 12.0 },
|
|
||||||
mat4::Column_T { 13.0, 14.0, 15.0, 16.0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto rhs = mat4 {
|
|
||||||
mat4::Column_T { 17.0, 18.0, 19.0, 20.0 },
|
|
||||||
mat4::Column_T { 21.0, 22.0, 23.0, 24.0 },
|
|
||||||
mat4::Column_T { 25.0, 26.0, 27.0, 28.0 },
|
|
||||||
mat4::Column_T { 29.0, 30.0, 31.0, 32.0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto [x, y, z, w] = (lhs * rhs).values;
|
|
||||||
|
|
||||||
expect_eq(x[0], 538.0);
|
|
||||||
expect_eq(x[1], 612.0);
|
|
||||||
expect_eq(x[2], 686.0);
|
|
||||||
expect_eq(x[3], 760.0);
|
|
||||||
|
|
||||||
expect_eq(y[0], 650.0);
|
|
||||||
expect_eq(y[1], 740.0);
|
|
||||||
expect_eq(y[2], 830.0);
|
|
||||||
expect_eq(y[3], 920.0);
|
|
||||||
|
|
||||||
expect_eq(z[0], 762.0);
|
|
||||||
expect_eq(z[1], 868.0);
|
|
||||||
expect_eq(z[2], 974.0);
|
|
||||||
expect_eq(z[3], 1080.0);
|
|
||||||
|
|
||||||
expect_eq(w[0], 874.0);
|
|
||||||
expect_eq(w[1], 996.0);
|
|
||||||
expect_eq(w[2], 1118.0);
|
|
||||||
expect_eq(w[3], 1240.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite access_operators = "mat4_access_operators"_suite = [] {
|
|
||||||
Case { "operator []" } = [] {
|
|
||||||
auto mat = mat4 {
|
|
||||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect_eq(mat[0][0], 1.0);
|
|
||||||
expect_eq(mat[0][1], 2.0);
|
|
||||||
expect_eq(mat[0][2], 3.0);
|
|
||||||
expect_eq(mat[0][3], 4.0);
|
|
||||||
|
|
||||||
expect_eq(mat[1][0], 5.0);
|
|
||||||
expect_eq(mat[1][1], 6.0);
|
|
||||||
expect_eq(mat[1][2], 7.0);
|
|
||||||
expect_eq(mat[1][3], 8.0);
|
|
||||||
|
|
||||||
expect_eq(mat[2][0], 9.0);
|
|
||||||
expect_eq(mat[2][1], 10.0);
|
|
||||||
expect_eq(mat[2][2], 11.0);
|
|
||||||
expect_eq(mat[2][3], 12.0);
|
|
||||||
|
|
||||||
expect_eq(mat[3][0], 13.0);
|
|
||||||
expect_eq(mat[3][1], 14.0);
|
|
||||||
expect_eq(mat[3][2], 15.0);
|
|
||||||
expect_eq(mat[3][3], 16.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator [] const" } = [] {
|
|
||||||
const auto mat = mat4 {
|
|
||||||
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect_eq(mat[0][0], 1.0);
|
|
||||||
expect_eq(mat[0][1], 2.0);
|
|
||||||
expect_eq(mat[0][2], 3.0);
|
|
||||||
expect_eq(mat[0][3], 4.0);
|
|
||||||
|
|
||||||
expect_eq(mat[1][0], 5.0);
|
|
||||||
expect_eq(mat[1][1], 6.0);
|
|
||||||
expect_eq(mat[1][2], 7.0);
|
|
||||||
expect_eq(mat[1][3], 8.0);
|
|
||||||
|
|
||||||
expect_eq(mat[2][0], 9.0);
|
|
||||||
expect_eq(mat[2][1], 10.0);
|
|
||||||
expect_eq(mat[2][2], 11.0);
|
|
||||||
expect_eq(mat[2][3], 12.0);
|
|
||||||
|
|
||||||
expect_eq(mat[3][0], 13.0);
|
|
||||||
expect_eq(mat[3][1], 14.0);
|
|
||||||
expect_eq(mat[3][2], 15.0);
|
|
||||||
expect_eq(mat[3][3], 16.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite transformations = "mat4_transformations"_suite = [] {
|
|
||||||
Case { "translate" } = [] {
|
|
||||||
const auto &[x, y, z, w] = mat4::translate(vec3 { 1, 2, 3 }).values;
|
|
||||||
|
|
||||||
// identity basis
|
|
||||||
expect_eq(x[0], 1);
|
|
||||||
expect_eq(x[1], 0);
|
|
||||||
expect_eq(x[2], 0);
|
|
||||||
expect_eq(x[3], 0);
|
|
||||||
expect_eq(y[0], 0);
|
|
||||||
expect_eq(y[1], 1);
|
|
||||||
expect_eq(y[2], 0);
|
|
||||||
expect_eq(y[3], 0);
|
|
||||||
expect_eq(z[0], 0);
|
|
||||||
expect_eq(z[1], 0);
|
|
||||||
expect_eq(z[2], 1);
|
|
||||||
expect_eq(z[3], 0);
|
|
||||||
|
|
||||||
// translation column
|
|
||||||
expect_eq(w[0], 1);
|
|
||||||
expect_eq(w[1], 2);
|
|
||||||
expect_eq(w[2], 3);
|
|
||||||
expect_eq(w[3], 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "scale" } = [] {
|
|
||||||
const auto [x, y, z, w] = mat4::scale(vec3 { 2, 3, 4 }).values;
|
|
||||||
|
|
||||||
expect_eq(x[0], 2);
|
|
||||||
expect_eq(x[1], 0);
|
|
||||||
expect_eq(x[2], 0);
|
|
||||||
expect_eq(x[3], 0);
|
|
||||||
expect_eq(y[0], 0);
|
|
||||||
expect_eq(y[1], 3);
|
|
||||||
expect_eq(y[2], 0);
|
|
||||||
expect_eq(y[3], 0);
|
|
||||||
expect_eq(z[0], 0);
|
|
||||||
expect_eq(z[1], 0);
|
|
||||||
expect_eq(z[2], 4);
|
|
||||||
expect_eq(z[3], 0);
|
|
||||||
expect_eq(w[0], 0);
|
|
||||||
expect_eq(w[1], 0);
|
|
||||||
expect_eq(w[2], 0);
|
|
||||||
expect_eq(w[3], 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "scale -> translate" } = [] {
|
|
||||||
const auto scale = mat4::scale(vec3 { 2, 2, 2 });
|
|
||||||
const auto translate = mat4::translate(vec3 { 1, 2, 3 });
|
|
||||||
const auto [x, y, z, w] = (scale * translate).values;
|
|
||||||
|
|
||||||
// scaled basis
|
|
||||||
expect_eq(x[0], 2);
|
|
||||||
expect_eq(x[1], 0);
|
|
||||||
expect_eq(x[2], 0);
|
|
||||||
expect_eq(x[3], 0);
|
|
||||||
expect_eq(y[0], 0);
|
|
||||||
expect_eq(y[1], 2);
|
|
||||||
expect_eq(y[2], 0);
|
|
||||||
expect_eq(y[3], 0);
|
|
||||||
expect_eq(z[0], 0);
|
|
||||||
expect_eq(z[1], 0);
|
|
||||||
expect_eq(z[2], 2);
|
|
||||||
expect_eq(z[3], 0);
|
|
||||||
|
|
||||||
// translation is scaled (local-space translation)
|
|
||||||
expect_eq(w[0], 2); // 1 * 2
|
|
||||||
expect_eq(w[1], 4); // 2 * 2
|
|
||||||
expect_eq(w[2], 6); // 3 * 2
|
|
||||||
expect_eq(w[3], 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "transpose" } = [] {
|
|
||||||
const auto mat = mat4 {
|
|
||||||
mat4::Column_T { 1, 2, 3, 4 },
|
|
||||||
mat4::Column_T { 5, 6, 7, 8 },
|
|
||||||
mat4::Column_T { 9, 10, 11, 12 },
|
|
||||||
mat4::Column_T { 13, 14, 15, 16 },
|
|
||||||
};
|
|
||||||
const auto [x, y, z, w] = mat4::transpose(mat).values;
|
|
||||||
|
|
||||||
// rows become columns
|
|
||||||
expect_eq(x[0], 1);
|
|
||||||
expect_eq(x[1], 5);
|
|
||||||
expect_eq(x[2], 9);
|
|
||||||
expect_eq(x[3], 13);
|
|
||||||
expect_eq(y[0], 2);
|
|
||||||
expect_eq(y[1], 6);
|
|
||||||
expect_eq(y[2], 10);
|
|
||||||
expect_eq(y[3], 14);
|
|
||||||
expect_eq(z[0], 3);
|
|
||||||
expect_eq(z[1], 7);
|
|
||||||
expect_eq(z[2], 11);
|
|
||||||
expect_eq(z[3], 15);
|
|
||||||
expect_eq(w[0], 4);
|
|
||||||
expect_eq(w[1], 8);
|
|
||||||
expect_eq(w[2], 12);
|
|
||||||
expect_eq(w[3], 16);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "transpose twice" } = [] {
|
|
||||||
const auto mat = mat4 {
|
|
||||||
mat4::Column_T { 1, 2, 3, 4 },
|
|
||||||
mat4::Column_T { 5, 6, 7, 8 },
|
|
||||||
mat4::Column_T { 9, 10, 11, 12 },
|
|
||||||
mat4::Column_T { 13, 14, 15, 16 },
|
|
||||||
};
|
|
||||||
const auto [x, y, z, w] = mat4::transpose(mat4::transpose(mat)).values;
|
|
||||||
|
|
||||||
expect_eq(x[0], 1);
|
|
||||||
expect_eq(x[1], 2);
|
|
||||||
expect_eq(x[2], 3);
|
|
||||||
expect_eq(x[3], 4);
|
|
||||||
expect_eq(y[0], 5);
|
|
||||||
expect_eq(y[1], 6);
|
|
||||||
expect_eq(y[2], 7);
|
|
||||||
expect_eq(y[3], 8);
|
|
||||||
expect_eq(z[0], 9);
|
|
||||||
expect_eq(z[1], 10);
|
|
||||||
expect_eq(z[2], 11);
|
|
||||||
expect_eq(z[3], 12);
|
|
||||||
expect_eq(w[0], 13);
|
|
||||||
expect_eq(w[1], 14);
|
|
||||||
expect_eq(w[2], 15);
|
|
||||||
expect_eq(w[3], 16);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -4,22 +4,22 @@ import preliminary;
|
||||||
|
|
||||||
export namespace lt::math {
|
export namespace lt::math {
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto to_radians(f32 degrees) -> f32
|
[[nodiscard]] constexpr auto radians(f32 degrees) -> f32
|
||||||
{
|
{
|
||||||
return degrees * 0.01745329251994329576923690768489f;
|
return degrees * 0.01745329251994329576923690768489f;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto to_radians(f64 degrees) -> f64
|
[[nodiscard]] constexpr auto radians(f64 degrees) -> f64
|
||||||
{
|
{
|
||||||
return degrees * 0.01745329251994329576923690768489;
|
return degrees * 0.01745329251994329576923690768489;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto to_degrees(f32 radians) -> f32
|
[[nodiscard]] constexpr auto degrees(f32 radians) -> f32
|
||||||
{
|
{
|
||||||
return radians * 57.295779513082320876798154814105f;
|
return radians * 57.295779513082320876798154814105f;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto to_degrees(f64 radians) -> f64
|
[[nodiscard]] constexpr auto degrees(f64 radians) -> f64
|
||||||
{
|
{
|
||||||
return radians * 57.295779513082320876798154814105;
|
return radians * 57.295779513082320876798154814105;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import test;
|
|
||||||
import math.trig;
|
|
||||||
|
|
||||||
Suite conversions = "trig_conversions"_suite = [] {
|
|
||||||
using ::lt::math::to_degrees;
|
|
||||||
using ::lt::math::to_radians;
|
|
||||||
|
|
||||||
Case { "to_radians <f32>" } = [] {
|
|
||||||
expect_eq(to_radians(f32 { 0.0f }), f32 { 0.0f });
|
|
||||||
expect_eq(to_radians(f32 { 90.0f }), f32 { 1.5707963267948966f });
|
|
||||||
expect_eq(to_radians(f32 { 180.0f }), f32 { 3.1415926535897932f });
|
|
||||||
expect_eq(to_radians(f32 { 360.0f }), f32 { 6.2831853071795864f });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "to_radians <f64>" } = [] {
|
|
||||||
expect_eq(to_radians(f64 { 0.0 }), f64 { 0.0 });
|
|
||||||
expect_eq(to_radians(f64 { 90.0 }), f64 { 1.5707963267948966 });
|
|
||||||
expect_eq(to_radians(f64 { 180.0 }), f64 { 3.1415926535897932 });
|
|
||||||
expect_eq(to_radians(f64 { 360.0 }), f64 { 6.2831853071795864 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "to_degrees <f32>" } = [] {
|
|
||||||
expect_eq(to_degrees(f32 { 0.0f }), f32 { 0.0f });
|
|
||||||
expect_eq(to_degrees(f32 { 1.5707963267948966f }), f32 { 90.0f });
|
|
||||||
expect_eq(to_degrees(f32 { 3.1415926535897932f }), f32 { 180.0f });
|
|
||||||
expect_eq(to_degrees(f32 { 6.2831853071795864f }), f32 { 360.0f });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "to_degrees <f64>" } = [] {
|
|
||||||
expect_eq(to_degrees(f64 { 0.0 }), f64 { 0.0 });
|
|
||||||
expect_eq(to_degrees(f64 { 1.5707963267948966 }), f64 { 90.0 });
|
|
||||||
expect_eq(to_degrees(f64 { 3.1415926535897932 }), f64 { 180.0 });
|
|
||||||
expect_eq(to_degrees(f64 { 6.2831853071795864 }), f64 { 360.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "to_degrees -> to_radians -> to_degrees <f32>" } = [] {
|
|
||||||
expect_eq(to_degrees(to_radians(f32 { 45.0f })), f32 { 45.0f });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "to_degrees -> to_radians -> to_degrees <f64>" } = [] {
|
|
||||||
expect_eq(to_degrees(to_radians(f64 { 45.0 })), f64 { 45.0 });
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -8,8 +8,6 @@ template<typename T = f32>
|
||||||
requires(std::is_arithmetic_v<T>)
|
requires(std::is_arithmetic_v<T>)
|
||||||
struct vec2_impl
|
struct vec2_impl
|
||||||
{
|
{
|
||||||
using Underlying_T = T;
|
|
||||||
|
|
||||||
static constexpr auto num_elements = 2u;
|
static constexpr auto num_elements = 2u;
|
||||||
|
|
||||||
constexpr vec2_impl(): x(), y()
|
constexpr vec2_impl(): x(), y()
|
||||||
|
|
@ -68,35 +66,33 @@ struct vec2_impl
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
|
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
|
||||||
{
|
{
|
||||||
debug_check(idx < num_elements, "vec2 out of bound access: {}", idx);
|
debug_check(idx <= num_elements, "vec2 out of bound: {}", idx);
|
||||||
return ((T *)this)[idx];
|
return ((T *)this)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
|
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
|
||||||
{
|
{
|
||||||
debug_check(idx < num_elements, "vec2 out of bound access: {}", idx);
|
debug_check(idx < num_elements, "vec2 out of bound: {}", idx);
|
||||||
return ((T *)this)[idx];
|
return ((T *)this)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend auto operator<<(std::ostream &stream, vec2_impl<T> value) -> std::ostream &
|
||||||
|
{
|
||||||
|
stream << value.x << ", " << value.y;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
T x;
|
T x;
|
||||||
|
|
||||||
T y;
|
T y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
using vec2 = vec2_impl<f32>;
|
using vec2 = vec2_impl<f32>;
|
||||||
|
|
||||||
using vec2_f32 = vec2;
|
using ivec2 = vec2_impl<i32>;
|
||||||
using vec2_f64 = vec2_impl<f64>;
|
|
||||||
|
|
||||||
using vec2_i8 = vec2_impl<i8>;
|
using uvec2 = vec2_impl<u32>;
|
||||||
using vec2_i16 = vec2_impl<i16>;
|
|
||||||
using vec2_i32 = vec2_impl<i32>;
|
|
||||||
using vec2_i64 = vec2_impl<i64>;
|
|
||||||
|
|
||||||
using vec2_u8 = vec2_impl<u8>;
|
|
||||||
using vec2_u16 = vec2_impl<u16>;
|
|
||||||
using vec2_u32 = vec2_impl<u32>;
|
|
||||||
using vec2_u64 = vec2_impl<u64>;
|
|
||||||
|
|
||||||
} // namespace lt::math
|
} // namespace lt::math
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,130 +1,7 @@
|
||||||
import test;
|
import test;
|
||||||
import math.vec2;
|
import math.vec2;
|
||||||
|
|
||||||
using vec2 = ::lt::math::vec2;
|
Suite raii = "raii"_suite = [] {
|
||||||
using ivec2 = ::lt::math::vec2_i32;
|
Case { "happy path" } = [] {
|
||||||
|
|
||||||
Suite static_tests = "vec3_static_checks"_suite = [] {
|
|
||||||
constexpr auto num_elements = lt::math::vec2::num_elements;
|
|
||||||
|
|
||||||
static_assert(num_elements == 2u);
|
|
||||||
static_assert(std::is_same_v<lt::math::vec2, lt::math::vec2_f32>);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec2_f32) == sizeof(f32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec2_f64) == sizeof(f64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec2_i8) == sizeof(i8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec2_i16) == sizeof(i16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec2_i32) == sizeof(i32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec2_i64) == sizeof(i64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec2_u8) == sizeof(u8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec2_u16) == sizeof(u16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec2_u32) == sizeof(u32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec2_u64) == sizeof(u64) * num_elements);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite raii = "vec2_raii"_suite = [] {
|
|
||||||
Case { "happy paths" } = [] {
|
|
||||||
ignore = vec2 {};
|
|
||||||
ignore = vec2 { 2.0 };
|
|
||||||
ignore = vec2 { 2.0, 4.0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "unhappy paths" } = [] {
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "many" } = [] {
|
|
||||||
for (auto idx : std::views::iota(0, 1'000'000))
|
|
||||||
{
|
|
||||||
ignore = idx;
|
|
||||||
ignore = vec2 {};
|
|
||||||
ignore = vec2 { 2.0 };
|
|
||||||
ignore = vec2 { 2.0, 4.0 };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post default construct has correct state" } = [] {
|
|
||||||
const auto vec = vec2 {};
|
|
||||||
expect_eq(vec.x, 0.0);
|
|
||||||
expect_eq(vec.y, 0.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post scalar construct has correct state" } = [] {
|
|
||||||
const auto vec = vec2 { 2.0 };
|
|
||||||
expect_eq(vec.x, 2.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,y has correct state" } = [] {
|
|
||||||
const auto vec = vec2 { 2.0, 3.0 };
|
|
||||||
expect_eq(vec.x, 2.0);
|
|
||||||
expect_eq(vec.y, 3.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite arithmetic_operators = "vec2_operators"_suite = [] {
|
|
||||||
Case { "operator ==" } = [] {
|
|
||||||
const auto lhs = vec2 { 1.0, 2.0 };
|
|
||||||
|
|
||||||
expect_false(lhs == vec2 { {}, 2.0 });
|
|
||||||
expect_false(lhs == vec2 { 1.0, {} });
|
|
||||||
expect_true(lhs == vec2 { 1.0, 2.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator !=" } = [] {
|
|
||||||
const auto lhs = vec2 { 1.0, 2.0 };
|
|
||||||
|
|
||||||
expect_true(lhs != vec2 { {}, 2.0 });
|
|
||||||
expect_true(lhs != vec2 { 1.0, {} });
|
|
||||||
expect_false(lhs != vec2 { 1.0, 2.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator +" } = [] {
|
|
||||||
const auto lhs = vec2 { 2.0, 3.0 };
|
|
||||||
const auto rhs = vec2 { 4.0, 5.0 };
|
|
||||||
expect_eq(lhs + rhs, vec2 { 6.0, 8.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator -" } = [] {
|
|
||||||
const auto lhs = vec2 { 2.0, 3.0 };
|
|
||||||
const auto rhs = vec2 { 4.0, 6.0 };
|
|
||||||
expect_eq(lhs - rhs, vec2 { -2.0, -3.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator *" } = [] {
|
|
||||||
const auto lhs = vec2 { 2.0, 3.0 };
|
|
||||||
const auto rhs = vec2 { 10.0, 20.0 };
|
|
||||||
expect_eq(lhs * rhs, vec2 { 20.0, 60.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator /" } = [] {
|
|
||||||
const auto lhs = vec2 { 10.0, 20.0 };
|
|
||||||
const auto rhs = vec2 { 2.0, 20.0 };
|
|
||||||
expect_eq(lhs / rhs, vec2 { 5.0, 1.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator []" } = [] {
|
|
||||||
auto vec = vec2 { 0.0, 1.0 };
|
|
||||||
expect_eq(vec[0], 0.0);
|
|
||||||
expect_eq(vec[1], 1.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator [] const" } = [] {
|
|
||||||
const auto vec = vec2 { 0.0, 1.0 };
|
|
||||||
expect_eq(vec[0], 0.0);
|
|
||||||
expect_eq(vec[1], 1.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite utilities = "vec2_utilities"_suite = [] {
|
|
||||||
Case { "std::format float" } = [] {
|
|
||||||
auto str = std::format("{}", vec2 { 10.0000f, 30.0005f });
|
|
||||||
expect_eq(str, "10, 30.0005");
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "std::format int" } = [] {
|
|
||||||
auto str = std::format("{}", ivec2 { 10, 30 });
|
|
||||||
expect_eq(str, "10, 30");
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ template<typename T = f32>
|
||||||
requires(std::is_arithmetic_v<T>)
|
requires(std::is_arithmetic_v<T>)
|
||||||
struct vec3_impl
|
struct vec3_impl
|
||||||
{
|
{
|
||||||
using Underlying_T = T;
|
|
||||||
|
|
||||||
static constexpr auto num_elements = 3u;
|
static constexpr auto num_elements = 3u;
|
||||||
|
|
||||||
constexpr vec3_impl(): x(), y(), z()
|
constexpr vec3_impl(): x(), y(), z()
|
||||||
|
|
@ -29,7 +27,7 @@ struct vec3_impl
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr vec3_impl(T x, vec2_impl<T> yz): x(x), y(yz.x), z(yz.y)
|
constexpr vec3_impl(T x, vec2_impl<T> yz): x(x), y(yz.y), z(yz.z)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,13 +79,13 @@ struct vec3_impl
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
|
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
|
||||||
{
|
{
|
||||||
debug_check(idx < num_elements, "vec3 out of bound access: {}", idx);
|
debug_check(idx <= num_elements, "vec3 out of bound: {}", idx);
|
||||||
return ((T *)this)[idx];
|
return ((T *)this)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
|
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
|
||||||
{
|
{
|
||||||
debug_check(idx < num_elements, "vec3 out of bound access: {}", idx);
|
debug_check(idx < num_elements, "vec3 out of bound: {}", idx);
|
||||||
return ((T *)this)[idx];
|
return ((T *)this)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,18 +104,9 @@ struct vec3_impl
|
||||||
|
|
||||||
using vec3 = vec3_impl<f32>;
|
using vec3 = vec3_impl<f32>;
|
||||||
|
|
||||||
using vec3_f32 = vec3;
|
using ivec3 = vec3_impl<i32>;
|
||||||
using vec3_f64 = vec3_impl<f64>;
|
|
||||||
|
|
||||||
using vec3_i8 = vec3_impl<i8>;
|
using uvec3 = vec3_impl<u32>;
|
||||||
using vec3_i16 = vec3_impl<i16>;
|
|
||||||
using vec3_i32 = vec3_impl<i32>;
|
|
||||||
using vec3_i64 = vec3_impl<i64>;
|
|
||||||
|
|
||||||
using vec3_u8 = vec3_impl<u8>;
|
|
||||||
using vec3_u16 = vec3_impl<u16>;
|
|
||||||
using vec3_u32 = vec3_impl<u32>;
|
|
||||||
using vec3_u64 = vec3_impl<u64>;
|
|
||||||
|
|
||||||
} // namespace lt::math
|
} // namespace lt::math
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
import test;
|
|
||||||
import math.vec2;
|
|
||||||
import math.vec3;
|
|
||||||
|
|
||||||
using vec2 = ::lt::math::vec2;
|
|
||||||
using vec3 = ::lt::math::vec3;
|
|
||||||
using ivec3 = ::lt::math::vec3_i32;
|
|
||||||
|
|
||||||
Suite static_tests = "vec3_static_checks"_suite = [] {
|
|
||||||
constexpr auto num_elements = lt::math::vec3::num_elements;
|
|
||||||
|
|
||||||
static_assert(num_elements == 3u);
|
|
||||||
static_assert(std::is_same_v<lt::math::vec3, lt::math::vec3_f32>);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec3_f32) == sizeof(f32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec3_f64) == sizeof(f64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec3_i8) == sizeof(i8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec3_i16) == sizeof(i16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec3_i32) == sizeof(i32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec3_i64) == sizeof(i64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec3_u8) == sizeof(u8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec3_u16) == sizeof(u16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec3_u32) == sizeof(u32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec3_u64) == sizeof(u64) * num_elements);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite raii = "vec3_raii"_suite = [] {
|
|
||||||
Case { "happy paths" } = [] {
|
|
||||||
ignore = vec3 {};
|
|
||||||
ignore = vec3 { 2.0 };
|
|
||||||
ignore = vec3 { 2.0, 4.0, 6.0 };
|
|
||||||
ignore = vec3 { vec2 { 2.0, 4.0 }, 6.0 };
|
|
||||||
ignore = vec3 { 2.0, vec2 { 4.0, 6.0 } };
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "unhappy paths" } = [] {
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "many" } = [] {
|
|
||||||
for (auto idx : std::views::iota(0, 1'000'000))
|
|
||||||
{
|
|
||||||
ignore = idx;
|
|
||||||
ignore = vec3 {};
|
|
||||||
ignore = vec3 { 2.0 };
|
|
||||||
ignore = vec3 { 2.0, 4.0, 6.0 };
|
|
||||||
ignore = vec3 { vec2 { 2.0, 4.0 }, 6.0 };
|
|
||||||
ignore = vec3 { 2.0, vec2 { 4.0, 6.0 } };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post default construct has correct state" } = [] {
|
|
||||||
const auto vec = vec3 {};
|
|
||||||
expect_eq(vec.x, 0.0);
|
|
||||||
expect_eq(vec.y, 0.0);
|
|
||||||
expect_eq(vec.z, 0.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post scalar construct has correct state" } = [] {
|
|
||||||
const auto vec = vec3 { 2.0 };
|
|
||||||
expect_eq(vec.x, 2.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 2.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,y,z has correct state" } = [] {
|
|
||||||
const auto vec = vec3 { 1.0, 2.0, 3.0 };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.y, 3.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with xy,z has correct state" } = [] {
|
|
||||||
const auto vec = vec3 { vec2 { 1.0, 2.0 }, 3.0 };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,yz has correct state" } = [] {
|
|
||||||
const auto vec = vec3 { 1.0, vec2 { 2.0, 3.0 } };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite arithmetic_operators = "vec3_operators"_suite = [] {
|
|
||||||
Case { "operator ==" } = [] {
|
|
||||||
const auto lhs = vec3 { 1.0, 2.0, 3.0 };
|
|
||||||
|
|
||||||
expect_false(lhs == vec3 { {}, 2.0, 3.0 });
|
|
||||||
expect_false(lhs == vec3 { 1.0, {}, 3.0 });
|
|
||||||
expect_false(lhs == vec3 { 1.0, 2.0, {} });
|
|
||||||
expect_true(lhs == vec3 { 1.0, 2.0, 3.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator !=" } = [] {
|
|
||||||
const auto lhs = vec3 { 1.0, 2.0, 3.0 };
|
|
||||||
|
|
||||||
expect_true(lhs != vec3 { {}, 2.0, 3.0 });
|
|
||||||
expect_true(lhs != vec3 { 1.0, {}, 3.0 });
|
|
||||||
expect_true(lhs != vec3 { 1.0, 2.0, {} });
|
|
||||||
expect_false(lhs != vec3 { 1.0, 2.0, 3.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator +" } = [] {
|
|
||||||
const auto lhs = vec3 { 1.0, 2.0, 3.0 };
|
|
||||||
const auto rhs = vec3 { 4.0, 5.0, 6.0 };
|
|
||||||
expect_eq(lhs + rhs, vec3 { 5.0, 7.0, 9.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator -" } = [] {
|
|
||||||
const auto lhs = vec3 { 1.0, 2.0, 3.0 };
|
|
||||||
const auto rhs = vec3 { 4.0, 5.0, 7.0 };
|
|
||||||
expect_eq(lhs - rhs, vec3 { -2.0, -3.0, -4.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator *" } = [] {
|
|
||||||
const auto lhs = vec3 { 1.0, 2.0, 3.0 };
|
|
||||||
const auto rhs = vec3 { 4.0, 5.0, 6.0 };
|
|
||||||
expect_eq(lhs * rhs, vec3 { 4.0, 10.0, 18.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator /" } = [] {
|
|
||||||
const auto lhs = vec3 { 4.0, 10.0, 30.0 };
|
|
||||||
const auto rhs = vec3 { 1.0, 2.0, 5.0 };
|
|
||||||
expect_eq(lhs / rhs, vec3 { 4.0, 5.0, 6.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator []" } = [] {
|
|
||||||
auto vec = vec3 { 0.0, 1.0, 2.0 };
|
|
||||||
expect_eq(vec[0], 0.0);
|
|
||||||
expect_eq(vec[1], 1.0);
|
|
||||||
expect_eq(vec[2], 2.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator [] const" } = [] {
|
|
||||||
const auto vec = vec3 { 0.0, 1.0, 2.0 };
|
|
||||||
expect_eq(vec[0], 0.0);
|
|
||||||
expect_eq(vec[1], 1.0);
|
|
||||||
expect_eq(vec[2], 2.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite utilities = "vec3_utilities"_suite = [] {
|
|
||||||
Case { "std::format float" } = [] {
|
|
||||||
auto str = std::format("{}", vec3 { 10.0000f, 30.0005f, 40.00005f });
|
|
||||||
expect_eq(str, "10, 30.0005, 40.00005");
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "std::format int" } = [] {
|
|
||||||
auto str = std::format("{}", ivec3 { 10, 30, 3'000'000 });
|
|
||||||
expect_eq(str, "10, 30, 3000000");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -10,8 +10,6 @@ template<typename T = f32>
|
||||||
requires(std::is_arithmetic_v<T>)
|
requires(std::is_arithmetic_v<T>)
|
||||||
struct vec4_impl
|
struct vec4_impl
|
||||||
{
|
{
|
||||||
using Underlying_T = T;
|
|
||||||
|
|
||||||
static constexpr auto num_elements = 4u;
|
static constexpr auto num_elements = 4u;
|
||||||
|
|
||||||
constexpr vec4_impl(): x(), y(), z(), w()
|
constexpr vec4_impl(): x(), y(), z(), w()
|
||||||
|
|
@ -30,15 +28,11 @@ struct vec4_impl
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr vec4_impl(T x, vec2_impl<T> yz, T w): x(x), y(yz.x), z(yz.y), w(w)
|
constexpr vec4_impl(T x, T y, vec2_impl<T> zw): x(x), y(y), z(zw.z), w(zw.w)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr vec4_impl(T x, T y, vec2_impl<T> zw): x(x), y(y), z(zw.x), w(zw.y)
|
constexpr vec4_impl(vec2_impl<T> xy, vec2_impl<T> zw): x(xy.x), y(xy.y), z(zw.z), w(zw.w)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr vec4_impl(vec2_impl<T> xy, vec2_impl<T> zw): x(xy.x), y(xy.y), z(zw.x), w(zw.y)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +40,7 @@ struct vec4_impl
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr vec4_impl(T x, vec3_impl<T> yzw): x(x), y(yzw.x), z(yzw.y), w(yzw.z)
|
constexpr vec4_impl(T x, vec3_impl<T> yzw): x(x), y(yzw.y), z(yzw.z), w(yzw.w)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,13 +96,13 @@ struct vec4_impl
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
|
[[nodiscard]] constexpr auto operator[](u8 idx) -> T &
|
||||||
{
|
{
|
||||||
debug_check(idx < num_elements, "vec4 out of bound access: {}", idx);
|
debug_check(idx <= num_elements, "vec4 out of bound: {}", idx);
|
||||||
return ((T *)this)[idx];
|
return ((T *)this)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
|
[[nodiscard]] constexpr auto operator[](u8 idx) const -> const T &
|
||||||
{
|
{
|
||||||
debug_check(idx < num_elements, "vec4 out of bound access: {}", idx);
|
debug_check(idx < num_elements, "vec4 out of bound: {}", idx);
|
||||||
return ((T *)this)[idx];
|
return ((T *)this)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,18 +123,9 @@ struct vec4_impl
|
||||||
|
|
||||||
using vec4 = vec4_impl<f32>;
|
using vec4 = vec4_impl<f32>;
|
||||||
|
|
||||||
using vec4_f32 = vec4;
|
using ivec4 = vec4_impl<i32>;
|
||||||
using vec4_f64 = vec4_impl<f64>;
|
|
||||||
|
|
||||||
using vec4_i8 = vec4_impl<i8>;
|
using uvec4 = vec4_impl<u32>;
|
||||||
using vec4_i16 = vec4_impl<i16>;
|
|
||||||
using vec4_i32 = vec4_impl<i32>;
|
|
||||||
using vec4_i64 = vec4_impl<i64>;
|
|
||||||
|
|
||||||
using vec4_u8 = vec4_impl<u8>;
|
|
||||||
using vec4_u16 = vec4_impl<u16>;
|
|
||||||
using vec4_u32 = vec4_impl<u32>;
|
|
||||||
using vec4_u64 = vec4_impl<u64>;
|
|
||||||
|
|
||||||
} // namespace lt::math
|
} // namespace lt::math
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,215 +0,0 @@
|
||||||
import test;
|
|
||||||
import math.vec2;
|
|
||||||
import math.vec3;
|
|
||||||
import math.vec4;
|
|
||||||
import logger;
|
|
||||||
|
|
||||||
using vec2 = ::lt::math::vec2;
|
|
||||||
using vec3 = ::lt::math::vec3;
|
|
||||||
using vec4 = ::lt::math::vec4;
|
|
||||||
using ivec4 = ::lt::math::vec4_i32;
|
|
||||||
|
|
||||||
Suite static_tests = "vec4_static_checks"_suite = [] {
|
|
||||||
constexpr auto num_elements = lt::math::vec4::num_elements;
|
|
||||||
|
|
||||||
static_assert(num_elements == 4u);
|
|
||||||
static_assert(std::is_same_v<lt::math::vec4, lt::math::vec4_f32>);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec4_f32) == sizeof(f32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec4_f64) == sizeof(f64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec4_i8) == sizeof(i8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec4_i16) == sizeof(i16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec4_i32) == sizeof(i32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec4_i64) == sizeof(i64) * num_elements);
|
|
||||||
|
|
||||||
static_assert(sizeof(lt::math::vec4_u8) == sizeof(u8) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec4_u16) == sizeof(u16) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec4_u32) == sizeof(u32) * num_elements);
|
|
||||||
static_assert(sizeof(lt::math::vec4_u64) == sizeof(u64) * num_elements);
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite raii = "vec4_raii"_suite = [] {
|
|
||||||
Case { "happy paths" } = [] {
|
|
||||||
ignore = vec4 {};
|
|
||||||
ignore = vec4 { 2.0 };
|
|
||||||
ignore = vec4 { 2.0, 4.0, 6.0, 8.0 };
|
|
||||||
ignore = vec4 { vec2 { 2.0, 4.0 }, 6.0, 8.0 };
|
|
||||||
ignore = vec4 { 2.0, 4.0, vec2 { 6.0, 8.0 } };
|
|
||||||
ignore = vec4 { vec2 { 2.0, 4.0 }, vec2 { 6.0, 8.0 } };
|
|
||||||
ignore = vec4 { vec3 { 2.0, 4.0, 6.0 }, 8.0 };
|
|
||||||
ignore = vec4 { 2.0, vec3 { 4.0, 6.0, 8.0 } };
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "unhappy paths" } = [] {
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "many" } = [] {
|
|
||||||
for (auto idx : std::views::iota(0, 1'000'000))
|
|
||||||
{
|
|
||||||
ignore = idx;
|
|
||||||
ignore = vec4 {};
|
|
||||||
ignore = vec4 { 2.0 };
|
|
||||||
ignore = vec4 { 2.0, 4.0, 6.0, 8.0 };
|
|
||||||
ignore = vec4 { vec2 { 2.0, 4.0 }, 6.0, 8.0 };
|
|
||||||
ignore = vec4 { 2.0, 4.0, vec2 { 6.0, 8.0 } };
|
|
||||||
ignore = vec4 { vec2 { 2.0, 4.0 }, vec2 { 6.0, 8.0 } };
|
|
||||||
ignore = vec4 { vec3 { 2.0, 4.0, 6.0 }, 8.0 };
|
|
||||||
ignore = vec4 { 2.0, vec3 { 4.0, 6.0, 8.0 } };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post default construct has correct state" } = [] {
|
|
||||||
const auto vec = vec4 {};
|
|
||||||
expect_eq(vec.x, 0.0);
|
|
||||||
expect_eq(vec.y, 0.0);
|
|
||||||
expect_eq(vec.z, 0.0);
|
|
||||||
expect_eq(vec.w, 0.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post scalar construct has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { 2.0 };
|
|
||||||
expect_eq(vec.x, 2.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 2.0);
|
|
||||||
expect_eq(vec.w, 2.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,y,z,w has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { 1.0, 2.0, 3.0, 4.0 };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.y, 3.0);
|
|
||||||
expect_eq(vec.z, 4.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with xy,z,w has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { vec2 { 1.0, 2.0 }, 3.0, 4.0 };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
expect_eq(vec.w, 4.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,y,zw has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { 1.0, 2.0, vec2 { 3.0, 4.0 } };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
expect_eq(vec.w, 4.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,yz,w has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { 1.0, vec2 { 2.0, 3.0 }, 4.0 };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
expect_eq(vec.w, 4.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,y,zw has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { 1.0, 2.0, vec2 { 3.0, 4.0 } };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
expect_eq(vec.w, 4.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with xy,zw has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { vec2 { 1.0, 2.0 }, vec2 { 3.0, 4.0 } };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
expect_eq(vec.w, 4.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with xyz,w has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { vec3 { 1.0, 2.0, 3.0 }, 4.0 };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
expect_eq(vec.w, 4.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "post construct with x,yzw has correct state" } = [] {
|
|
||||||
const auto vec = vec4 { 1.0, vec3 { 2.0, 3.0, 4.0 } };
|
|
||||||
expect_eq(vec.x, 1.0);
|
|
||||||
expect_eq(vec.y, 2.0);
|
|
||||||
expect_eq(vec.z, 3.0);
|
|
||||||
expect_eq(vec.w, 4.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite arithmetic_operators = "vec4_operators"_suite = [] {
|
|
||||||
Case { "operator ==" } = [] {
|
|
||||||
const auto lhs = vec4 { 1.0, 2.0, 3.0, 4.0 };
|
|
||||||
|
|
||||||
expect_false(lhs == vec4 { {}, 2.0, 3.0, 4.0 });
|
|
||||||
expect_false(lhs == vec4 { 1.0, {}, 3.0, 4.0 });
|
|
||||||
expect_false(lhs == vec4 { 1.0, 2.0, {}, 4.0 });
|
|
||||||
expect_false(lhs == vec4 { 1.0, 2.0, 3.0, {} });
|
|
||||||
expect_true(lhs == vec4 { 1.0, 2.0, 3.0, 4.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator !=" } = [] {
|
|
||||||
const auto lhs = vec4 { 1.0, 2.0, 3.0, 4.0 };
|
|
||||||
|
|
||||||
expect_true(lhs != vec4 { {}, 2.0, 3.0, 4.0 });
|
|
||||||
expect_true(lhs != vec4 { 1.0, {}, 3.0, 4.0 });
|
|
||||||
expect_true(lhs != vec4 { 1.0, 2.0, {}, 4.0 });
|
|
||||||
expect_true(lhs != vec4 { 1.0, 2.0, 3.0, {} });
|
|
||||||
expect_false(lhs != vec4 { 1.0, 2.0, 3.0, 4.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator +" } = [] {
|
|
||||||
const auto lhs = vec4 { 1.0, 2.0, 3.0, 4.0 };
|
|
||||||
const auto rhs = vec4 { 5.0, 6.0, 7.0, 8.0 };
|
|
||||||
expect_eq(lhs + rhs, vec4 { 6.0, 8.0, 10.0, 12.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator -" } = [] {
|
|
||||||
const auto lhs = vec4 { 1.0, 2.0, 3.0, 4.0 };
|
|
||||||
const auto rhs = vec4 { 5.0, 10.0, 15.0, 20.0 };
|
|
||||||
expect_eq(lhs - rhs, vec4 { -4.0, -8.0, -12.0, -16.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator *" } = [] {
|
|
||||||
const auto lhs = vec4 { 1.0, 2.0, 3.0, 4.0 };
|
|
||||||
const auto rhs = vec4 { 5.0, 6.0, 7.0, 8.0 };
|
|
||||||
expect_eq(lhs * rhs, vec4 { 5.0, 12.0, 21.0, 32.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator /" } = [] {
|
|
||||||
const auto lhs = vec4 { 5.0, 6.0, 30.0, 8.0 };
|
|
||||||
const auto rhs = vec4 { 1.0, 2.0, 3.0, 4.0 };
|
|
||||||
expect_eq(lhs / rhs, vec4 { 5.0, 3.0, 10.0, 2.0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator []" } = [] {
|
|
||||||
auto vec = vec4 { 0.0, 1.0, 2.0, 3.0 };
|
|
||||||
expect_eq(vec[0], 0.0);
|
|
||||||
expect_eq(vec[1], 1.0);
|
|
||||||
expect_eq(vec[2], 2.0);
|
|
||||||
expect_eq(vec[3], 3.0);
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "operator [] const" } = [] {
|
|
||||||
const auto vec = vec4 { 0.0, 1.0, 2.0, 3.0 };
|
|
||||||
expect_eq(vec[0], 0.0);
|
|
||||||
expect_eq(vec[1], 1.0);
|
|
||||||
expect_eq(vec[2], 2.0);
|
|
||||||
expect_eq(vec[3], 3.0);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Suite utilities = "vec4_utilities"_suite = [] {
|
|
||||||
Case { "std::format float" } = [] {
|
|
||||||
auto str = std::format("{}", vec4 { 10.0000f, 30.0005f, 40.00005f, 0.0 });
|
|
||||||
expect_eq(str, "10, 30.0005, 40.00005, 0");
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "std::format int" } = [] {
|
|
||||||
auto str = std::format("{}", ivec4 { 10, 30, 3'000'000, 13 });
|
|
||||||
expect_eq(str, "10, 30, 3000000, 13");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import preliminary;
|
import preliminary;
|
||||||
import time;
|
import time;
|
||||||
import logger;
|
import test.expects;
|
||||||
import surface.system;
|
import surface.system;
|
||||||
import surface.events;
|
import surface.events;
|
||||||
import surface.requests;
|
import surface.requests;
|
||||||
|
|
@ -20,10 +20,6 @@ constexpr auto visible = false;
|
||||||
auto main() -> i32
|
auto main() -> i32
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (auto idx = 0; idx < 100; ++idx)
|
|
||||||
{
|
|
||||||
std::println("{}: \033[1;{}m| color |\033[0m", idx, idx);
|
|
||||||
}
|
|
||||||
auto registry = lt::memory::create_ref<lt::ecs::Registry>();
|
auto registry = lt::memory::create_ref<lt::ecs::Registry>();
|
||||||
auto system = lt::surface::System { registry };
|
auto system = lt::surface::System { registry };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1099,7 +1099,7 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
|
||||||
|
|
||||||
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
||||||
{
|
{
|
||||||
// log::debug("Setting window position to: {}, {}", request.position.x, request.position.y);
|
log::debug("Setting window position to: {}, {}", request.position.x, request.position.y);
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
surface.m_native_data.window,
|
surface.m_native_data.window,
|
||||||
{},
|
{},
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ Suite system_events = "system_events"_suite = [] {
|
||||||
};
|
};
|
||||||
|
|
||||||
Suite registry_events = "registry_events"_suite = [] {
|
Suite registry_events = "registry_events"_suite = [] {
|
||||||
Case { "on_construct initializes component" } = [] {
|
Case { "on_construct<SurfaceComponent> initializes component" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
|
|
||||||
const auto &component = fixture.create_component();
|
const auto &component = fixture.create_component();
|
||||||
|
|
@ -141,7 +141,7 @@ Suite registry_events = "registry_events"_suite = [] {
|
||||||
fixture.check_values(*component);
|
fixture.check_values(*component);
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "unhappy on_construct throws" } = [] {
|
Case { "unhappy on_construct<SurfaceComponent> throws" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
auto system = System { fixture.registry() };
|
auto system = System { fixture.registry() };
|
||||||
|
|
||||||
|
|
@ -168,7 +168,7 @@ Suite registry_events = "registry_events"_suite = [] {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "unhappy on_construct removes component" } = [] {
|
Case { "unhappy on_construct<SurfaceComponent> removes component" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
auto system = System { fixture.registry() };
|
auto system = System { fixture.registry() };
|
||||||
|
|
||||||
|
|
@ -176,7 +176,7 @@ Suite registry_events = "registry_events"_suite = [] {
|
||||||
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
|
expect_eq(fixture.registry()->view<SurfaceComponent>().get_size(), 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "on_destroy cleans up component" } = [] {
|
Case { "on_destrroy<SurfaceComponent> cleans up component" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
auto system = lt::memory::create_scope<System>(fixture.registry());
|
auto system = lt::memory::create_scope<System>(fixture.registry());
|
||||||
|
|
||||||
|
|
@ -189,21 +189,23 @@ Suite registry_events = "registry_events"_suite = [] {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Suite tick = "ticking"_suite = [] {
|
Suite tick = "tick"_suite = [] {
|
||||||
Case { "on empty registry won't throw" } = [] {
|
Case { "ticking on empty registry won't throw" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
System { fixture.registry() }.tick(tick_info());
|
System { fixture.registry() }.tick(tick_info());
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "on non-empty registry won't throw" } = [] {
|
Case { "ticking on non-empty registry won't throw" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
auto system = System { fixture.registry() };
|
auto system = System { fixture.registry() };
|
||||||
|
|
||||||
fixture.create_component();
|
fixture.create_component();
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
Case { "clears previous tick's events" } = [] {
|
Suite tick_handles_events = "tick_handles_events"_suite = [] {
|
||||||
|
Case { "ticking clears previous tick's events" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
auto system = System { fixture.registry() };
|
auto system = System { fixture.registry() };
|
||||||
auto &surface = **fixture.create_component();
|
auto &surface = **fixture.create_component();
|
||||||
|
|
@ -221,8 +223,10 @@ Suite tick = "ticking"_suite = [] {
|
||||||
system.tick(tick_info());
|
system.tick(tick_info());
|
||||||
expect_eq(surface.peek_events().size(), 0);
|
expect_eq(surface.peek_events().size(), 0);
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
Case { "clears requests" } = [] {
|
Suite tick_handles_requests = "tick_handles_requests"_suite = [] {
|
||||||
|
Case { "ticking clears requests" } = [] {
|
||||||
auto fixture = Fixture {};
|
auto fixture = Fixture {};
|
||||||
auto system = System { fixture.registry() };
|
auto system = System { fixture.registry() };
|
||||||
auto &surface = **fixture.create_component();
|
auto &surface = **fixture.create_component();
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ void print_help()
|
||||||
auto main(i32 argc, char **argv) -> i32
|
auto main(i32 argc, char **argv) -> i32
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
lt::log::set_min_severity(lt::log::Level::test);
|
|
||||||
auto raw_arguments = std::span<char *>(argv, argc);
|
auto raw_arguments = std::span<char *>(argv, argc);
|
||||||
|
|
||||||
auto options = lt::test::Registry::Options {};
|
auto options = lt::test::Registry::Options {};
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ export void expect_unreachable(
|
||||||
{
|
{
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format(
|
std::format(
|
||||||
"unreachable reached: {}:{}",
|
"Failed unreachable expectation:\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
source_location.file_name(),
|
source_location.file_name(),
|
||||||
source_location.line()
|
source_location.line()
|
||||||
),
|
),
|
||||||
|
|
@ -47,7 +48,12 @@ export constexpr void expect_throw(
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format("did not throw: {}:{}", source_location.file_name(), source_location.line()),
|
std::format(
|
||||||
|
"Failed throwing expectation:\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
|
source_location.file_name(),
|
||||||
|
source_location.line()
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +69,10 @@ export constexpr void expect_eq(
|
||||||
{
|
{
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format(
|
std::format(
|
||||||
"expect_eq: {} == {} @ {}:{}",
|
"Failed equality expectation:\n"
|
||||||
|
"\tactual: {}\n"
|
||||||
|
"\texpected: {}\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
std::to_underlying<decltype(lhs)>(lhs),
|
std::to_underlying<decltype(lhs)>(lhs),
|
||||||
std::to_underlying<decltype(rhs)>(rhs),
|
std::to_underlying<decltype(rhs)>(rhs),
|
||||||
source_location.file_name(),
|
source_location.file_name(),
|
||||||
|
|
@ -76,7 +85,10 @@ export constexpr void expect_eq(
|
||||||
{
|
{
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format(
|
std::format(
|
||||||
"expect_eq: {} == {} @ {}:{}",
|
"Failed equality expectation:\n"
|
||||||
|
"\tactual: {}\n"
|
||||||
|
"\texpected: {}\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
lhs,
|
lhs,
|
||||||
rhs,
|
rhs,
|
||||||
source_location.file_name(),
|
source_location.file_name(),
|
||||||
|
|
@ -96,47 +108,10 @@ export constexpr void expect_ne(
|
||||||
{
|
{
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format(
|
std::format(
|
||||||
"expect_ne: {} != {} @ {}:{}",
|
"Failed un-equality expectation:\n"
|
||||||
lhs,
|
"\tactual: {}\n"
|
||||||
rhs,
|
"\texpected: {}\n"
|
||||||
source_location.file_name(),
|
"\tlocation: {}:{}",
|
||||||
source_location.line()
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export constexpr void expect_le(
|
|
||||||
Testable auto lhs,
|
|
||||||
Testable auto rhs,
|
|
||||||
std::source_location source_location = std::source_location::current()
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (lhs > rhs)
|
|
||||||
{
|
|
||||||
throw std::runtime_error {
|
|
||||||
std::format(
|
|
||||||
"expect_le: {} <= {} @ {}:{}",
|
|
||||||
lhs,
|
|
||||||
rhs,
|
|
||||||
source_location.file_name(),
|
|
||||||
source_location.line()
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export constexpr void expect_ge(
|
|
||||||
Testable auto lhs,
|
|
||||||
Testable auto rhs,
|
|
||||||
std::source_location source_location = std::source_location::current()
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (lhs < rhs)
|
|
||||||
{
|
|
||||||
throw std::runtime_error {
|
|
||||||
std::format(
|
|
||||||
"expect_ge: {} >= {} @ {}:{}",
|
|
||||||
lhs,
|
lhs,
|
||||||
rhs,
|
rhs,
|
||||||
source_location.file_name(),
|
source_location.file_name(),
|
||||||
|
|
@ -155,7 +130,10 @@ export constexpr void expect_true(
|
||||||
{
|
{
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format(
|
std::format(
|
||||||
"expect_true: {} @ {}:{}",
|
"Failed true expectation:\n"
|
||||||
|
"\tactual: {}\n"
|
||||||
|
"\texpected: true\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
expression,
|
expression,
|
||||||
source_location.file_name(),
|
source_location.file_name(),
|
||||||
source_location.line()
|
source_location.line()
|
||||||
|
|
@ -173,7 +151,10 @@ export constexpr void expect_false(
|
||||||
{
|
{
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format(
|
std::format(
|
||||||
"expect_false: {} @ {}:{}",
|
"Failed false expectation:\n"
|
||||||
|
"\tactual: {}\n"
|
||||||
|
"\texpected: true\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
expression,
|
expression,
|
||||||
source_location.file_name(),
|
source_location.file_name(),
|
||||||
source_location.line()
|
source_location.line()
|
||||||
|
|
@ -191,7 +172,34 @@ export constexpr void expect_not_nullptr(
|
||||||
{
|
{
|
||||||
throw std::runtime_error {
|
throw std::runtime_error {
|
||||||
std::format(
|
std::format(
|
||||||
"expect_not_nullptr: @ {}:{}",
|
"Failed true expectation:\n"
|
||||||
|
"\tactual: nullptr\n"
|
||||||
|
"\texpected: not nullptr\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
|
source_location.file_name(),
|
||||||
|
source_location.line()
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export constexpr void expect_le(
|
||||||
|
Testable auto lhs,
|
||||||
|
Testable auto rhs,
|
||||||
|
std::source_location source_location = std::source_location::current()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (lhs > rhs)
|
||||||
|
{
|
||||||
|
throw std::runtime_error {
|
||||||
|
std::format(
|
||||||
|
"Failed false expectation:\n"
|
||||||
|
"\tactual: {}\n"
|
||||||
|
"\texpected: >= {}\n"
|
||||||
|
"\tlocation: {}:{}",
|
||||||
|
lhs,
|
||||||
|
rhs,
|
||||||
source_location.file_name(),
|
source_location.file_name(),
|
||||||
source_location.line()
|
source_location.line()
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
export module test.registry;
|
export module test.registry;
|
||||||
|
|
||||||
import logger;
|
|
||||||
import preliminary;
|
import preliminary;
|
||||||
import test.expects;
|
import test.expects;
|
||||||
|
|
||||||
|
|
@ -205,17 +204,17 @@ namespace lt::test {
|
||||||
++instance().m_failed_case_count;
|
++instance().m_failed_case_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ [[nodiscard]] auto Registry::should_return_on_failure() -> bool
|
[[nodiscard]] /* static */ auto Registry::should_return_on_failure() -> bool
|
||||||
{
|
{
|
||||||
return instance().m_options.stop_on_fail;
|
return instance().m_options.stop_on_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ [[nodiscard]] auto Registry::get_options() -> const Options &
|
[[nodiscard]] /* static */ auto Registry::get_options() -> const Options &
|
||||||
{
|
{
|
||||||
return instance().m_options;
|
return instance().m_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ [[nodiscard]] auto Registry::get_case_regex() -> const std::regex &
|
[[nodiscard]] /* static */ auto Registry::get_case_regex() -> const std::regex &
|
||||||
{
|
{
|
||||||
return instance().m_case_regex;
|
return instance().m_case_regex;
|
||||||
}
|
}
|
||||||
|
|
@ -232,30 +231,6 @@ auto Registry::run_all_impl() -> i32
|
||||||
{
|
{
|
||||||
if (std::regex_search(name, regex))
|
if (std::regex_search(name, regex))
|
||||||
{
|
{
|
||||||
auto padding_left = std::string {};
|
|
||||||
padding_left.resize((79 - std::strlen(name)) / 2u - 1u);
|
|
||||||
for (auto &ch : padding_left)
|
|
||||||
{
|
|
||||||
ch = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
auto padding_right = std::string {};
|
|
||||||
padding_right.resize((79 - std::strlen(name)) / 2u);
|
|
||||||
if (std::strlen(name) % 2 == 0)
|
|
||||||
{
|
|
||||||
padding_right.resize(padding_right.size() + 1);
|
|
||||||
}
|
|
||||||
for (auto &ch : padding_right)
|
|
||||||
{
|
|
||||||
ch = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
log::test(
|
|
||||||
"\033[1;33m*{}{}{}-*\033[0m",
|
|
||||||
std::string { padding_left },
|
|
||||||
std::string_view { name },
|
|
||||||
std::string { padding_right }
|
|
||||||
);
|
|
||||||
suite();
|
suite();
|
||||||
increment_matched_suite_count();
|
increment_matched_suite_count();
|
||||||
}
|
}
|
||||||
|
|
@ -270,12 +245,12 @@ auto Registry::run_all_impl() -> i32
|
||||||
{
|
{
|
||||||
if (m_options.stop_on_fail)
|
if (m_options.stop_on_fail)
|
||||||
{
|
{
|
||||||
log::info("Quitting due to options.stop_on_fail == true");
|
std::println("Quitting due to options.stop_on_fail == true");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::test("Uncaught exception when running suite:");
|
std::println("Uncaught exception when running suite:");
|
||||||
log::test("\twhat: {}", exp.what());
|
std::println("\twhat: {}", exp.what());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -284,32 +259,32 @@ auto Registry::run_all_impl() -> i32
|
||||||
{
|
{
|
||||||
case ExecutionPolicy::normal:
|
case ExecutionPolicy::normal:
|
||||||
{
|
{
|
||||||
// log::test("[-------STATS------]");
|
std::println("[-------STATS------]");
|
||||||
//
|
|
||||||
// log::test("suites:");
|
|
||||||
// log::test("\ttotal: {}", (i32)m_total_suite_count);
|
|
||||||
// log::test("\tpassed: {}", (i32)m_passed_suite_count);
|
|
||||||
// log::test("\tfailed: {}", (i32)m_failed_suite_count);
|
|
||||||
// log::test("\tmatched: {}", (i32)m_matched_suite_count);
|
|
||||||
// log::test("\tskipped: {}", (i32)m_skipped_suite_count);
|
|
||||||
//
|
|
||||||
// log::test("tests:");
|
|
||||||
// log::test("\ttotal: {}", (i32)m_total_case_count);
|
|
||||||
// log::test("\tpassed: {}", (i32)m_passed_case_count);
|
|
||||||
// log::test("\tfailed: {}", (i32)m_failed_case_count);
|
|
||||||
// log::test("\tmatched: {}", (i32)m_matched_case_count);
|
|
||||||
// log::test("\tskipped: {}", (i32)m_skipped_case_count);
|
|
||||||
|
|
||||||
// log::test("________________________________________________________________");
|
std::println("suites:");
|
||||||
|
std::println("\ttotal: {}", m_total_suite_count);
|
||||||
|
std::println("\tpassed: {}", m_passed_suite_count);
|
||||||
|
std::println("\tfailed: {}", m_failed_suite_count);
|
||||||
|
std::println("\tmatched: {}", m_matched_suite_count);
|
||||||
|
std::println("\tskipped: {}", m_skipped_suite_count);
|
||||||
|
|
||||||
|
std::println("tests:");
|
||||||
|
std::println("\ttotal: {}", m_total_case_count);
|
||||||
|
std::println("\tpassed: {}", m_passed_case_count);
|
||||||
|
std::println("\tfailed: {}", m_failed_case_count);
|
||||||
|
std::println("\tmatched: {}", m_matched_case_count);
|
||||||
|
std::println("\tskipped: {}", m_skipped_case_count);
|
||||||
|
|
||||||
|
std::println("________________________________________________________________");
|
||||||
|
|
||||||
return m_failed_case_count;
|
return m_failed_case_count;
|
||||||
}
|
}
|
||||||
case ExecutionPolicy::stats:
|
case ExecutionPolicy::stats:
|
||||||
{
|
{
|
||||||
log::test("[-------STATS------]");
|
std::println("[-------STATS------]");
|
||||||
log::test("Total suite count: {}", (i32)m_total_suite_count);
|
std::println("Total suite count: {}", m_total_suite_count);
|
||||||
log::test("Total test count: {}", (i32)m_total_case_count);
|
std::println("Total test count: {}", m_total_case_count);
|
||||||
log::test("________________________________________________________________");
|
std::println("________________________________________________________________");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -320,12 +295,12 @@ auto Registry::run_all_impl() -> i32
|
||||||
|
|
||||||
void Registry::print_options()
|
void Registry::print_options()
|
||||||
{
|
{
|
||||||
// log::info("stop-on-failure: {}", static_cast<bool>(m_options.stop_on_fail));
|
std::println("stop-on-failure: {}", m_options.stop_on_fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
Registry::Registry()
|
Registry::Registry()
|
||||||
{
|
{
|
||||||
// log::info("________________________________________________________________");
|
std::println("________________________________________________________________");
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] /* static */ auto Registry::instance() -> Registry &
|
[[nodiscard]] /* static */ auto Registry::instance() -> Registry &
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ export module test.test;
|
||||||
import test.expects;
|
import test.expects;
|
||||||
import test.registry;
|
import test.registry;
|
||||||
import preliminary;
|
import preliminary;
|
||||||
import logger;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// ----------* INTERFACE *--------- //
|
// ----------* INTERFACE *--------- //
|
||||||
|
|
@ -14,7 +12,7 @@ namespace lt::test {
|
||||||
class TestCase
|
class TestCase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TestCase(std::string name);
|
TestCase(std::string_view name);
|
||||||
|
|
||||||
// NOLINTNEXTLINE(misc-unconventional-assign-operator)
|
// NOLINTNEXTLINE(misc-unconventional-assign-operator)
|
||||||
auto operator=(std::invocable auto test) const -> void;
|
auto operator=(std::invocable auto test) const -> void;
|
||||||
|
|
@ -23,7 +21,7 @@ private:
|
||||||
void run_normal(std::invocable auto test) const;
|
void run_normal(std::invocable auto test) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_name;
|
std::string_view m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TestSuite
|
struct TestSuite
|
||||||
|
|
@ -70,26 +68,16 @@ void TestCase::run_normal(std::invocable auto test) const
|
||||||
}
|
}
|
||||||
Registry::increment_matched_case_count();
|
Registry::increment_matched_case_count();
|
||||||
|
|
||||||
auto padding = std::string {};
|
std::println("[Running-----------] --> ");
|
||||||
padding.resize(79 - m_name.size());
|
std::println("{}", m_name);
|
||||||
for (auto &ch : padding)
|
|
||||||
{
|
|
||||||
ch = ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
test();
|
test();
|
||||||
}
|
}
|
||||||
catch (const std::exception &exp)
|
catch (const std::exception &exp)
|
||||||
{
|
{
|
||||||
log::test(
|
std::println("{}", exp.what());
|
||||||
"\033[1;31m{}{} | {}\033[0m",
|
std::println("[-----------FAIL !!]");
|
||||||
std::string_view { m_name },
|
|
||||||
std::string { padding },
|
|
||||||
std::string { exp.what() }
|
|
||||||
);
|
|
||||||
|
|
||||||
Registry::increment_failed_case_count();
|
Registry::increment_failed_case_count();
|
||||||
|
|
||||||
if (Registry::should_return_on_failure())
|
if (Registry::should_return_on_failure())
|
||||||
|
|
@ -101,9 +89,7 @@ void TestCase::run_normal(std::invocable auto test) const
|
||||||
}
|
}
|
||||||
|
|
||||||
Registry::increment_passed_case_count();
|
Registry::increment_passed_case_count();
|
||||||
|
std::println("[--------SUCCESS :D]");
|
||||||
|
|
||||||
log::test("{}{} | \033[1;32mpass\033[0m", std::string_view { m_name }, std::string { padding });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TestSuite::TestSuite(auto body)
|
TestSuite::TestSuite(auto body)
|
||||||
|
|
@ -137,13 +123,8 @@ auto operator""_suite(const char *name, size_t size) -> TestSuite
|
||||||
module :private;
|
module :private;
|
||||||
namespace lt::test {
|
namespace lt::test {
|
||||||
|
|
||||||
TestCase::TestCase(std::string name): m_name(name)
|
TestCase::TestCase(std::string_view name): m_name(name)
|
||||||
{
|
{
|
||||||
if (m_name.size() > 79u)
|
|
||||||
{
|
|
||||||
m_name.resize(79u - 3);
|
|
||||||
m_name.append("...");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace lt::test
|
} // namespace lt::test
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,7 @@
|
||||||
import test;
|
import test;
|
||||||
|
|
||||||
Suite expects = "expects"_suite = []() {
|
Suite expects = "expects"_suite = []() {
|
||||||
// should be truncated...
|
Case { "" } = [] {
|
||||||
Case { "berryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy "
|
|
||||||
"long name" }
|
|
||||||
= [] {
|
|
||||||
};
|
|
||||||
|
|
||||||
Case { "this emptiness machine" } = [] {
|
|
||||||
expect_le(9, 6);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "expect_unreachable" } = [] {
|
Case { "expect_unreachable" } = [] {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue