1
0
mirror of https://github.com/slendidev/smath.git synced 2026-03-17 02:26:50 +02:00

Add .clang-format and format codebase

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2026-03-11 23:52:54 +02:00
parent 01538457a5
commit 4f71b3ac90
8 changed files with 526 additions and 411 deletions

51
.clang-format Normal file
View File

@@ -0,0 +1,51 @@
---
Language: Cpp
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: ForIndentation
ColumnLimit: 80
AlignAfterOpenBracket: DontAlign
AlignOperands: false
BreakBeforeBinaryOperators: All
ContinuationIndentWidth: 4
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: Never
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: false
BeforeElse: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortBlocksOnASingleLine: Empty
SpaceInEmptyBlock: true
BinPackArguments: false
BinPackParameters: false
Cpp11BracedListStyle: false
SpaceBeforeCpp11BracedList: true
IndentRequiresClause: false
RequiresClausePosition: OwnLine
PointerAlignment: Right
ReferenceAlignment: Right
IndentAccessModifiers: false
AccessModifierOffset: -4
SortIncludes: CaseSensitive
SpaceAfterTemplateKeyword: false
AlignEscapedNewlines: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
...

View File

@@ -13,7 +13,8 @@
#include <smath.hpp> #include <smath.hpp>
using smath::Vec2; using smath::Vec2;
enum Color : uint8_t { enum Color : uint8_t
{
CLR_NONE = 0, // default CLR_NONE = 0, // default
CLR_AXES = 90, // bright black (gray) CLR_AXES = 90, // bright black (gray)
CLR_A = 32, // green CLR_A = 32, // green
@@ -22,18 +23,21 @@ enum Color : uint8_t {
CLR_DOT = 36 // cyan CLR_DOT = 36 // cyan
}; };
struct Cell { struct Cell
char ch{' '}; {
uint8_t color{CLR_NONE}; char ch { ' ' };
int prio{0}; uint8_t color { CLR_NONE };
int prio { 0 };
}; };
struct Canvas { struct Canvas
{
int w, h; int w, h;
std::vector<Cell> pix; std::vector<Cell> pix;
Canvas(int W, int H) : w(W), h(H), pix(W * H) {} Canvas(int W, int H) : w(W), h(H), pix(W * H) { }
void put(int x, int y, char c, int prio, uint8_t color) { void put(int x, int y, char c, int prio, uint8_t color)
{
if (x < 0 || x >= w || y < 0 || y >= h) if (x < 0 || x >= w || y < 0 || y >= h)
return; return;
Cell &cell = pix[y * w + x]; Cell &cell = pix[y * w + x];
@@ -43,16 +47,19 @@ struct Canvas {
cell.color = color; cell.color = color;
} }
} }
void hline(int y, char c, int prio, uint8_t color) { void hline(int y, char c, int prio, uint8_t color)
{
for (int x = 0; x < w; ++x) for (int x = 0; x < w; ++x)
put(x, y, c, prio, color); put(x, y, c, prio, color);
} }
void vline(int x, char c, int prio, uint8_t color) { void vline(int x, char c, int prio, uint8_t color)
{
for (int y = 0; y < h; ++y) for (int y = 0; y < h; ++y)
put(x, y, c, prio, color); put(x, y, c, prio, color);
} }
void line_dir(int x0, int y0, int x1, int y1, int prio, uint8_t color) { void line_dir(int x0, int y0, int x1, int y1, int prio, uint8_t color)
{
int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1; int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1; int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy; int err = dx + dy;
@@ -92,7 +99,8 @@ struct Canvas {
} }
} }
void flush() const { void flush() const
{
uint8_t cur = 255; uint8_t cur = 255;
for (int y = 0; y < h; ++y) { for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) { for (int x = 0; x < w; ++x) {
@@ -112,19 +120,23 @@ struct Canvas {
} }
}; };
struct Mapper { struct Mapper
{
int W, H; int W, H;
double scale; double scale;
int cx, cy; int cx, cy;
Mapper(int w, int h, double s) : W(w), H(h), scale(s), cx(w / 2), cy(h / 2) {} Mapper(int w, int h, double s) : W(w), H(h), scale(s), cx(w / 2), cy(h / 2)
std::pair<int, int> map(Vec2 v) const { { }
std::pair<int, int> map(Vec2 v) const
{
int x = cx + (int)std::llround(v.x() * scale); int x = cx + (int)std::llround(v.x() * scale);
int y = cy - (int)std::llround(v.y() * scale); int y = cy - (int)std::llround(v.y() * scale);
return {x, y}; return { x, y };
} }
}; };
int main() { int main()
{
#if defined(_WIN32) #if defined(_WIN32)
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
if (h != INVALID_HANDLE_VALUE) { if (h != INVALID_HANDLE_VALUE) {
@@ -143,27 +155,29 @@ int main() {
if (!(std::cin >> bx >> by)) if (!(std::cin >> bx >> by))
return 0; return 0;
Vec2 a{(float)ax, (float)ay}; Vec2 a { (float)ax, (float)ay };
Vec2 b{(float)bx, (float)by}; Vec2 b { (float)bx, (float)by };
Vec2 p = a.project_onto(b); Vec2 p = a.project_onto(b);
std::println("\nResults:"); std::println("\nResults:");
std::println("a = {}", a); std::println("a = {}", a);
std::println("b = {}", b); std::println("b = {}", b);
std::println("proj_b(a) = p = {}", p); std::println("proj_b(a) = p = {}", p);
std::println("|a|={} |b|={} |p|={}", a.magnitude(), b.magnitude(), std::println(
p.magnitude()); "|a|={} |b|={} |p|={}", a.magnitude(), b.magnitude(), p.magnitude());
double dot = a.dot(b); double dot = a.dot(b);
double ang = (a.magnitude() > 0 && b.magnitude() > 0) double ang = (a.magnitude() > 0 && b.magnitude() > 0)
? std::acos(std::clamp(dot / (a.magnitude() * b.magnitude()), ? std::acos(
-1.0, 1.0)) * std::clamp(dot / (a.magnitude() * b.magnitude()), -1.0, 1.0))
180.0 / M_PI * 180.0 / M_PI
: 0.0; : 0.0;
std::println("a·b={} angle(a,b)={} deg\n", dot, ang); std::println("a·b={} angle(a,b)={} deg\n", dot, ang);
const int W = 73, H = 33; const int W = 73, H = 33;
double maxr = std::max({(double)a.magnitude(), (double)b.magnitude(), double maxr = std::max({ (double)a.magnitude(),
(double)p.magnitude(), 1.0}); (double)b.magnitude(),
(double)p.magnitude(),
1.0 });
double usable = 0.45 * std::min(W, H); double usable = 0.45 * std::min(W, H);
double scale = usable / maxr; double scale = usable / maxr;
@@ -171,12 +185,12 @@ int main() {
Mapper mp(W, H, scale); Mapper mp(W, H, scale);
int pr_axes = 1; int pr_axes = 1;
auto [cx, cy] = mp.map({0, 0}); auto [cx, cy] = mp.map({ 0, 0 });
cvs.hline(H / 2, '-', pr_axes, CLR_AXES); cvs.hline(H / 2, '-', pr_axes, CLR_AXES);
cvs.vline(W / 2, '|', pr_axes, CLR_AXES); cvs.vline(W / 2, '|', pr_axes, CLR_AXES);
cvs.put(W / 2, H / 2, 'O', pr_axes + 1, CLR_AXES); cvs.put(W / 2, H / 2, 'O', pr_axes + 1, CLR_AXES);
auto [ox, oy] = mp.map({0, 0}); auto [ox, oy] = mp.map({ 0, 0 });
auto [ax1, ay1] = mp.map(a); auto [ax1, ay1] = mp.map(a);
auto [bx1, by1] = mp.map(b); auto [bx1, by1] = mp.map(b);
auto [px1, py1] = mp.map(p); auto [px1, py1] = mp.map(p);

View File

@@ -24,16 +24,17 @@
#include <smath.hpp> #include <smath.hpp>
auto main() -> int { auto main() -> int
{
using namespace smath; using namespace smath;
Vec2d point; Vec2d point;
std::random_device rd; std::random_device rd;
std::mt19937 rng{rd()}; std::mt19937 rng { rd() };
std::uniform_real_distribution<> dis(-5, 5); std::uniform_real_distribution<> dis(-5, 5);
int i = 0; int i = 0;
do { do {
Vec2d add{dis(rng), dis(rng)}; Vec2d add { dis(rng), dis(rng) };
auto const n = point + add; auto const n = point + add;
std::println("{}: {:.2f} + {:.2f} -> {:.2f}", i, point, add, n); std::println("{}: {:.2f} + {:.2f} -> {:.2f}", i, point, add, n);
point = n; point = n;

View File

@@ -19,9 +19,10 @@
#include <smath.hpp> #include <smath.hpp>
int main() { int main()
{
using namespace smath; using namespace smath;
Vec3 v{1, 2, 3}; Vec3 v { 1, 2, 3 };
std::println("v: {}", v); std::println("v: {}", v);
auto v2 = swizzle<"zyx">(v); auto v2 = swizzle<"zyx">(v);
std::println("v2: {}", v2); std::println("v2: {}", v2);
@@ -39,7 +40,7 @@ int main() {
std::println("std::get<1>(v): {}", std::get<1>(v)); std::println("std::get<1>(v): {}", std::get<1>(v));
auto [x, y, z] = v; auto [x, y, z] = v;
std::println("Bindings: [{}, {}, {}]", x, y, z); std::println("Bindings: [{}, {}, {}]", x, y, z);
float x1{}, y1{}, z1{}; float x1 {}, y1 {}, z1 {};
v.unpack(x1, y1, z1); v.unpack(x1, y1, z1);
std::println("Unpacked: {}, {}, {}", x1, y1, z1); std::println("Unpacked: {}, {}, {}", x1, y1, z1);

View File

@@ -32,20 +32,23 @@
#include <type_traits> #include <type_traits>
#ifndef SMATH_ANGLE_UNIT #ifndef SMATH_ANGLE_UNIT
# define SMATH_ANGLE_UNIT rad #define SMATH_ANGLE_UNIT rad
#endif // SMATH_ANGLE_UNIT #endif // SMATH_ANGLE_UNIT
namespace smath { namespace smath
{
template<std::size_t N, typename T> template<std::size_t N, typename T>
requires std::is_arithmetic_v<T> struct Vec; requires std::is_arithmetic_v<T>
struct Vec;
namespace detail { namespace detail
{
#define SMATH_STR(x) #x #define SMATH_STR(x) #x
#define SMATH_XSTR(x) SMATH_STR(x) #define SMATH_XSTR(x) SMATH_STR(x)
consteval bool streq(const char *a, const char *b) consteval auto streq(const char *a, const char *b) -> bool
{ {
for (;; ++a, ++b) { for (;; ++a, ++b) {
if (*a != *b) if (*a != *b)
@@ -55,13 +58,14 @@ consteval bool streq(const char *a, const char *b)
} }
} }
enum class AngularUnit { enum class AngularUnit
{
Radians, Radians,
Degrees, Degrees,
Turns, Turns,
}; };
consteval std::optional<AngularUnit> parse_unit(char const *s) consteval auto parse_unit(char const *s) -> std::optional<AngularUnit>
{ {
if (streq(s, "rad")) if (streq(s, "rad"))
return AngularUnit::Radians; return AngularUnit::Radians;
@@ -76,7 +80,8 @@ constexpr auto SMATH_ANGLE_UNIT_ID = parse_unit(SMATH_XSTR(SMATH_ANGLE_UNIT));
static_assert(SMATH_ANGLE_UNIT_ID != std::nullopt, static_assert(SMATH_ANGLE_UNIT_ID != std::nullopt,
"Invalid SMATH_ANGLE_UNIT. Should be rad, deg, or turns."); "Invalid SMATH_ANGLE_UNIT. Should be rad, deg, or turns.");
template<std::size_t N> struct FixedString { template<std::size_t N> struct FixedString
{
char data[N] {}; char data[N] {};
static constexpr std::size_t size = N - 1; static constexpr std::size_t size = N - 1;
constexpr FixedString(char const (&s)[N]) constexpr FixedString(char const (&s)[N])
@@ -86,8 +91,10 @@ template<std::size_t N> struct FixedString {
} }
constexpr char operator[](std::size_t i) const { return data[i]; } constexpr char operator[](std::size_t i) const { return data[i]; }
}; };
template<class X> struct is_Vec : std::false_type { }; template<class X> struct is_Vec : std::false_type
template<std::size_t M, class U> struct is_Vec<Vec<M, U>> : std::true_type { }; { };
template<std::size_t M, class U> struct is_Vec<Vec<M, U>> : std::true_type
{ };
template<class X> template<class X>
inline constexpr bool is_Vec_v = is_Vec<std::remove_cvref_t<X>>::value; inline constexpr bool is_Vec_v = is_Vec<std::remove_cvref_t<X>>::value;
template<class X> template<class X>
@@ -95,7 +102,8 @@ inline constexpr bool is_scalar_v
= std::is_arithmetic_v<std::remove_cvref_t<X>>; = std::is_arithmetic_v<std::remove_cvref_t<X>>;
template<class X> struct Vec_size; template<class X> struct Vec_size;
template<std::size_t M, class U> template<std::size_t M, class U>
struct Vec_size<Vec<M, U>> : std::integral_constant<std::size_t, M> { }; struct Vec_size<Vec<M, U>> : std::integral_constant<std::size_t, M>
{ };
template<class T> constexpr auto pack_unorm8(T v) -> std::uint8_t template<class T> constexpr auto pack_unorm8(T v) -> std::uint8_t
{ {
@@ -144,9 +152,11 @@ template<class T> constexpr auto unpack_snorm8(std::int8_t b) -> T
} // namespace detail } // namespace detail
template<std::size_t N, typename T = float> template<std::size_t N, typename T = float>
requires std::is_arithmetic_v<T> struct Vec : std::array<T, N> { requires std::is_arithmetic_v<T>
struct Vec : std::array<T, N>
{
private: private:
template<class X> static consteval std::size_t extent() template<class X> static consteval auto extent() -> std::size_t
{ {
if constexpr (detail::is_Vec_v<X>) if constexpr (detail::is_Vec_v<X>)
return detail::Vec_size<std::remove_cvref_t<X>>::value; return detail::Vec_size<std::remove_cvref_t<X>>::value;
@@ -155,7 +165,7 @@ private:
else else
return 0; // Should be unreachable return 0; // Should be unreachable
} }
template<class... Args> static consteval std::size_t total_extent() template<class... Args> static consteval auto total_extent() -> std::size_t
{ {
return (extent<Args>() + ... + 0); return (extent<Args>() + ... + 0);
} }
@@ -188,12 +198,9 @@ public:
// NOTE: This can (probably) be improved with C++26 reflection in the // NOTE: This can (probably) be improved with C++26 reflection in the
// future. // future.
#define VEC_ACC(component, req, idx) \ #define VEC_ACC(component, req, idx) \
constexpr auto component() noexcept -> T & \ constexpr auto component() noexcept -> T &requires(N >= req) { \
requires(N >= req) \
{ \
return (*this)[idx]; \ return (*this)[idx]; \
} \ } constexpr auto component() const->T const & \
constexpr auto component() const -> T const & \
requires(N >= req) \ requires(N >= req) \
{ \ { \
return (*this)[idx]; \ return (*this)[idx]; \
@@ -225,7 +232,8 @@ public:
((args = (*this)[Is]), ...); ((args = (*this)[Is]), ...);
} }
template<class... Args> constexpr void unpack(Args &...args) noexcept template<class... Args>
constexpr auto unpack(Args &...args) noexcept -> void
{ {
unpack_impl(std::index_sequence_for<Args...> {}, args...); unpack_impl(std::index_sequence_for<Args...> {}, args...);
} }
@@ -284,13 +292,13 @@ public:
VEC_OP(/) VEC_OP(/)
#undef VEC_OP #undef VEC_OP
#define VEC_OP_ASSIGN(sym) \ #define VEC_OP_ASSIGN(sym) \
constexpr Vec &operator sym## = (Vec const &rhs) noexcept \ constexpr Vec &operator sym##=(Vec const &rhs) noexcept \
{ \ { \
for (std::size_t i = 0; i < N; ++i) \ for (std::size_t i = 0; i < N; ++i) \
(*this)[i] sym## = rhs[i]; \ (*this)[i] sym## = rhs[i]; \
return *this; \ return *this; \
} \ } \
constexpr Vec &operator sym## = (T const &s) noexcept \ constexpr Vec &operator sym##=(T const &s) noexcept \
{ \ { \
for (std::size_t i = 0; i < N; ++i) \ for (std::size_t i = 0; i < N; ++i) \
(*this)[i] sym## = s; \ (*this)[i] sym## = s; \
@@ -393,11 +401,14 @@ public:
} }
template<typename U = T> template<typename U = T>
requires(N == 3) constexpr auto cross(Vec const &r) const noexcept -> Vec requires(N == 3)
constexpr auto cross(Vec const &r) const noexcept -> Vec
{ {
return { (*this)[1] * r[2] - (*this)[2] * r[1], return {
(*this)[1] * r[2] - (*this)[2] * r[1],
(*this)[2] * r[0] - (*this)[0] * r[2], (*this)[2] * r[0] - (*this)[0] * r[2],
(*this)[0] * r[1] - (*this)[1] * r[0] }; (*this)[0] * r[1] - (*this)[1] * r[0],
};
} }
constexpr auto distance(Vec const &r) const noexcept -> T constexpr auto distance(Vec const &r) const noexcept -> T
@@ -425,8 +436,8 @@ public:
template<class U> template<class U>
requires(std::is_arithmetic_v<U> && N >= 1) requires(std::is_arithmetic_v<U> && N >= 1)
constexpr explicit(!std::is_convertible_v<T, U>) constexpr explicit(
operator Vec<N, U>() const noexcept !std::is_convertible_v<T, U>) operator Vec<N, U>() const noexcept
{ {
Vec<N, U> r {}; Vec<N, U> r {};
for (std::size_t i = 0; i < N; ++i) for (std::size_t i = 0; i < N; ++i)
@@ -444,26 +455,26 @@ public:
} }
private: private:
constexpr void fill_one(std::size_t &i, T const &v) noexcept constexpr auto fill_one(std::size_t &i, T const &v) noexcept -> void
{ {
(*this)[i++] = v; (*this)[i++] = v;
} }
#ifdef SMATH_IMPLICIT_CONVERSIONS #ifdef SMATH_IMPLICIT_CONVERSIONS
template<class U> template<class U>
requires std::is_arithmetic_v<U> && (!std::is_same_v<U, T>)constexpr void requires std::is_arithmetic_v<U> && (!std::is_same_v<U, T>)
fill_one(std::size_t &i, const U &v) noexcept constexpr auto fill_one(std::size_t &i, const U &v) noexcept -> void
{ {
(*this)[i++] = static_cast<T>(v); (*this)[i++] = static_cast<T>(v);
} }
template<std::size_t M, class U> template<std::size_t M, class U>
constexpr void fill_one(std::size_t &i, Vec<M, U> const &v) noexcept constexpr auto fill_one(std::size_t &i, Vec<M, U> const &v) noexcept -> void
{ {
for (std::size_t k = 0; k < M; ++k) for (std::size_t k = 0; k < M; ++k)
(*this)[i++] = static_cast<T>(v[k]); (*this)[i++] = static_cast<T>(v[k]);
} }
#endif // SMATH_IMPLICIT_CONVERSIONS #endif // SMATH_IMPLICIT_CONVERSIONS
template<std::size_t M> template<std::size_t M>
constexpr void fill_one(std::size_t &i, const Vec<M, T> &v) noexcept constexpr auto fill_one(std::size_t &i, const Vec<M, T> &v) noexcept -> void
{ {
for (std::size_t k = 0; k < M; ++k) for (std::size_t k = 0; k < M; ++k)
(*this)[i++] = static_cast<T>(v[k]); (*this)[i++] = static_cast<T>(v[k]);
@@ -497,7 +508,8 @@ template<std::size_t N, typename T = float>
requires std::is_arithmetic_v<T> requires std::is_arithmetic_v<T>
using VecOrScalar = std::conditional_t<N == 1, T, Vec<N, T>>; using VecOrScalar = std::conditional_t<N == 1, T, Vec<N, T>>;
namespace detail { namespace detail
{
consteval auto char_to_idx(char c) -> std::size_t consteval auto char_to_idx(char c) -> std::size_t
{ {
@@ -627,9 +639,11 @@ template<class T> constexpr auto turns(T value)
} }
} }
template<std::size_t R, std::size_t C, typename T> template<std::size_t R, std::size_t C, typename T>
requires std::is_arithmetic_v<T> struct Mat; requires std::is_arithmetic_v<T>
struct Mat;
template<class T> struct Quaternion : Vec<4, T> { template<class T> struct Quaternion : Vec<4, T>
{
using Base = Vec<4, T>; using Base = Vec<4, T>;
using Base::Base; using Base::Base;
using Base::operator=; using Base::operator=;
@@ -759,7 +773,9 @@ requires std::is_floating_point_v<T>
} }
template<std::size_t R, std::size_t C, typename T = float> template<std::size_t R, std::size_t C, typename T = float>
requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> { requires std::is_arithmetic_v<T>
struct Mat : std::array<Vec<R, T>, C>
{
using Base = std::array<Vec<R, T>, C>; using Base = std::array<Vec<R, T>, C>;
using Base::operator[]; using Base::operator[];
@@ -792,10 +808,8 @@ requires std::is_arithmetic_v<T> struct Mat : std::array<Vec<R, T>, C> {
template<typename... Cols> template<typename... Cols>
requires(sizeof...(Cols) == C requires(sizeof...(Cols) == C
&& (std::same_as<std::remove_cvref_t<Cols>, Vec<R, T>> && ...)) && (std::same_as<std::remove_cvref_t<Cols>, Vec<R, T>> && ...))
constexpr Mat(Cols const &...cols) noexcept constexpr Mat(Cols const &...cols) noexcept : Base { cols... }
: Base { cols... } { }
{
}
constexpr auto col(std::size_t j) noexcept -> Vec<R, T> & constexpr auto col(std::size_t j) noexcept -> Vec<R, T> &
{ {
@@ -1100,8 +1114,12 @@ template<typename T>
} }
template<typename T> template<typename T>
[[nodiscard]] inline auto matrix_ortho3d(T const left, T const right, [[nodiscard]] inline auto matrix_ortho3d(T const left,
T const bottom, T const top, T const near, T const far, T const right,
T const bottom,
T const top,
T const near,
T const far,
bool const flip_z_axis = true) -> Mat<4, 4, T> bool const flip_z_axis = true) -> Mat<4, 4, T>
{ {
Mat<4, 4, T> res {}; Mat<4, 4, T> res {};
@@ -1148,8 +1166,9 @@ inline auto matrix_perspective(
} }
template<typename T> template<typename T>
[[nodiscard]] inline auto matrix_look_at(Vec<3, T> const eye, [[nodiscard]] inline auto matrix_look_at(
Vec<3, T> const center, Vec<3, T> const up) -> Mat<4, 4, T> Vec<3, T> const eye, Vec<3, T> const center, Vec<3, T> const up)
-> Mat<4, 4, T>
{ {
auto f = (center - eye).normalized_safe(); auto f = (center - eye).normalized_safe();
auto s = f.cross(up).normalized_safe(); auto s = f.cross(up).normalized_safe();
@@ -1164,8 +1183,9 @@ template<typename T>
} }
template<typename T> template<typename T>
[[nodiscard]] inline auto matrix_infinite_perspective(T const fovy, [[nodiscard]] inline auto matrix_infinite_perspective(
T const aspect, T const znear, bool flip_z_axis = false) -> Mat<4, 4, T> T const fovy, T const aspect, T const znear, bool flip_z_axis = false)
-> Mat<4, 4, T>
{ {
Mat<4, 4, T> m {}; Mat<4, 4, T> m {};
@@ -1186,12 +1206,11 @@ template<typename T>
return m; return m;
} }
template<class Ext> struct interop_adapter; template<class Ext> struct interop_adapter;
template<class Ext> template<class Ext>
constexpr auto from_external(Ext const &ext) constexpr auto from_external(Ext const &ext) ->
-> typename interop_adapter<Ext>::smath_type typename interop_adapter<Ext>::smath_type
{ {
return interop_adapter<Ext>::to_smath(ext); return interop_adapter<Ext>::to_smath(ext);
} }
@@ -1202,12 +1221,12 @@ constexpr auto to_external(SMathT const &value) -> Ext
return interop_adapter<Ext>::from_smath(value); return interop_adapter<Ext>::from_smath(value);
} }
} // namespace smath } // namespace smath
template<std::size_t N, typename T> template<std::size_t N, typename T>
requires std::formattable<T, char> requires std::formattable<T, char>
struct std::formatter<smath::Vec<N, T>> : std::formatter<T> { struct std::formatter<smath::Vec<N, T>> : std::formatter<T>
{
constexpr auto parse(std::format_parse_context &ctx) constexpr auto parse(std::format_parse_context &ctx)
{ {
return std::formatter<T>::parse(ctx); return std::formatter<T>::parse(ctx);
@@ -1230,12 +1249,14 @@ struct std::formatter<smath::Vec<N, T>> : std::formatter<T> {
} }
}; };
namespace std { namespace std
{
template<size_t N, class T> template<size_t N, class T>
struct tuple_size<smath::Vec<N, T>> : std::integral_constant<size_t, N> { }; struct tuple_size<smath::Vec<N, T>> : std::integral_constant<size_t, N>
{ };
template<size_t I, size_t N, class T> template<size_t I, size_t N, class T> struct tuple_element<I, smath::Vec<N, T>>
struct tuple_element<I, smath::Vec<N, T>> { {
static_assert(I < N); static_assert(I < N);
using type = T; using type = T;
}; };

View File

@@ -2,14 +2,17 @@
#include <smath.hpp> #include <smath.hpp>
TEST(AngleReturnRadians, DegInput) { TEST(AngleReturnRadians, DegInput)
{
EXPECT_NEAR(smath::deg(180.0), std::numbers::pi, 1e-12); EXPECT_NEAR(smath::deg(180.0), std::numbers::pi, 1e-12);
} }
TEST(AngleReturnRadians, RadInput) { TEST(AngleReturnRadians, RadInput)
{
EXPECT_DOUBLE_EQ(smath::rad(std::numbers::pi), std::numbers::pi); EXPECT_DOUBLE_EQ(smath::rad(std::numbers::pi), std::numbers::pi);
} }
TEST(AngleReturnRadians, TurnsInput) { TEST(AngleReturnRadians, TurnsInput)
{
EXPECT_NEAR(smath::turns(0.5), std::numbers::pi, 1e-12); EXPECT_NEAR(smath::turns(0.5), std::numbers::pi, 1e-12);
} }

View File

@@ -4,22 +4,26 @@
#include <smath.hpp> #include <smath.hpp>
struct ExternalVec3f { struct ExternalVec3f
{
float x; float x;
float y; float y;
float z; float z;
}; };
struct ExternalMat2f { struct ExternalMat2f
{
float m00; float m00;
float m01; float m01;
float m10; float m10;
float m11; float m11;
}; };
namespace smath { namespace smath
{
template<> struct interop_adapter<ExternalVec3f> { template<> struct interop_adapter<ExternalVec3f>
{
using smath_type = Vec<3, float>; using smath_type = Vec<3, float>;
static constexpr auto to_smath(ExternalVec3f const &v) -> smath_type static constexpr auto to_smath(ExternalVec3f const &v) -> smath_type
@@ -33,7 +37,8 @@ template<> struct interop_adapter<ExternalVec3f> {
} }
}; };
template<> struct interop_adapter<ExternalMat2f> { template<> struct interop_adapter<ExternalMat2f>
{
using smath_type = Mat<2, 2, float>; using smath_type = Mat<2, 2, float>;
static constexpr auto to_smath(ExternalMat2f const &m) -> smath_type static constexpr auto to_smath(ExternalMat2f const &m) -> smath_type
@@ -54,7 +59,8 @@ template<> struct interop_adapter<ExternalMat2f> {
} // namespace smath } // namespace smath
TEST(Interop, FromExternalVec) { TEST(Interop, FromExternalVec)
{
ExternalVec3f ext { 1.0f, 2.0f, 3.0f }; ExternalVec3f ext { 1.0f, 2.0f, 3.0f };
auto v = smath::from_external(ext); auto v = smath::from_external(ext);
@@ -64,7 +70,8 @@ TEST(Interop, FromExternalVec) {
EXPECT_EQ(v[2], 3.0f); EXPECT_EQ(v[2], 3.0f);
} }
TEST(Interop, ToExternalVec) { TEST(Interop, ToExternalVec)
{
smath::Vec3 v { 4.0f, 5.0f, 6.0f }; smath::Vec3 v { 4.0f, 5.0f, 6.0f };
auto ext = smath::to_external<ExternalVec3f>(v); auto ext = smath::to_external<ExternalVec3f>(v);
@@ -73,7 +80,8 @@ TEST(Interop, ToExternalVec) {
EXPECT_EQ(ext.z, 6.0f); EXPECT_EQ(ext.z, 6.0f);
} }
TEST(Interop, RoundtripVec) { TEST(Interop, RoundtripVec)
{
ExternalVec3f ext { 7.0f, 8.0f, 9.0f }; ExternalVec3f ext { 7.0f, 8.0f, 9.0f };
auto v = smath::from_external(ext); auto v = smath::from_external(ext);
auto ext2 = smath::to_external<ExternalVec3f>(v); auto ext2 = smath::to_external<ExternalVec3f>(v);
@@ -83,7 +91,8 @@ TEST(Interop, RoundtripVec) {
EXPECT_EQ(ext2.z, 9.0f); EXPECT_EQ(ext2.z, 9.0f);
} }
TEST(Interop, MatrixConversion) { TEST(Interop, MatrixConversion)
{
ExternalMat2f ext { 1.0f, 2.0f, 3.0f, 4.0f }; ExternalMat2f ext { 1.0f, 2.0f, 3.0f, 4.0f };
auto m = smath::from_external(ext); auto m = smath::from_external(ext);

View File

@@ -11,50 +11,56 @@ using smath::Vec2;
using smath::Vec3; using smath::Vec3;
using smath::Vec4; using smath::Vec4;
template <class T> template<class T>
static void ExpectVecNear(const Vec<3, T> &a, const Vec<3, T> &b, static void ExpectVecNear(
T eps = T(1e-6)) { const Vec<3, T> &a, const Vec<3, T> &b, T eps = T(1e-6))
{
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
EXPECT_NEAR(double(a[i]), double(b[i]), double(eps)); EXPECT_NEAR(double(a[i]), double(b[i]), double(eps));
} }
// Constructors and accessors // Constructors and accessors
TEST(Vec, DefaultZero) { TEST(Vec, DefaultZero)
{
Vec3 v; Vec3 v;
EXPECT_EQ(v[0], 0.0f); EXPECT_EQ(v[0], 0.0f);
EXPECT_EQ(v[1], 0.0f); EXPECT_EQ(v[1], 0.0f);
EXPECT_EQ(v[2], 0.0f); EXPECT_EQ(v[2], 0.0f);
} }
TEST(Vec, ScalarFillCtor) { TEST(Vec, ScalarFillCtor)
Vec4 v{2.0f}; {
Vec4 v { 2.0f };
EXPECT_EQ(v.x(), 2.0f); EXPECT_EQ(v.x(), 2.0f);
EXPECT_EQ(v.y(), 2.0f); EXPECT_EQ(v.y(), 2.0f);
EXPECT_EQ(v.z(), 2.0f); EXPECT_EQ(v.z(), 2.0f);
EXPECT_EQ(v.w(), 2.0f); EXPECT_EQ(v.w(), 2.0f);
} }
TEST(Vec, VariadicCtorScalarsAndSubvectors) { TEST(Vec, VariadicCtorScalarsAndSubvectors)
Vec2 a{1.0f, 2.0f}; {
Vec2 b{3.0f, 4.0f}; Vec2 a { 1.0f, 2.0f };
Vec4 v{a, b}; Vec2 b { 3.0f, 4.0f };
Vec4 v { a, b };
EXPECT_EQ(v.r(), 1.0f); EXPECT_EQ(v.r(), 1.0f);
EXPECT_EQ(v.g(), 2.0f); EXPECT_EQ(v.g(), 2.0f);
EXPECT_EQ(v.b(), 3.0f); EXPECT_EQ(v.b(), 3.0f);
EXPECT_EQ(v.a(), 4.0f); EXPECT_EQ(v.a(), 4.0f);
} }
TEST(Vec, NamedAccessorsAliases) { TEST(Vec, NamedAccessorsAliases)
Vec3 v{1.0f, 2.0f, 3.0f}; {
Vec3 v { 1.0f, 2.0f, 3.0f };
EXPECT_EQ(v.x(), v.r()); EXPECT_EQ(v.x(), v.r());
EXPECT_EQ(v.y(), v.g()); EXPECT_EQ(v.y(), v.g());
EXPECT_EQ(v.z(), v.b()); EXPECT_EQ(v.z(), v.b());
} }
// Arithmetic // Arithmetic
TEST(Vec, ElementwiseAndScalarOps) { TEST(Vec, ElementwiseAndScalarOps)
Vec3 a{1.0f, 2.0f, 3.0f}; {
Vec3 b{4.0f, 5.0f, 6.0f}; Vec3 a { 1.0f, 2.0f, 3.0f };
Vec3 b { 4.0f, 5.0f, 6.0f };
auto s1 = a + b; auto s1 = a + b;
EXPECT_EQ(s1[0], 5.0f); EXPECT_EQ(s1[0], 5.0f);
@@ -71,8 +77,8 @@ TEST(Vec, ElementwiseAndScalarOps) {
EXPECT_EQ(s3[1], 4.0f); EXPECT_EQ(s3[1], 4.0f);
EXPECT_EQ(s3[2], 6.0f); EXPECT_EQ(s3[2], 6.0f);
Vec3 c{1.0f, 2.0f, 3.0f}; Vec3 c { 1.0f, 2.0f, 3.0f };
c += Vec3{1.0f, 1.0f, 1.0f}; c += Vec3 { 1.0f, 1.0f, 1.0f };
EXPECT_EQ(c[0], 2.0f); EXPECT_EQ(c[0], 2.0f);
EXPECT_EQ(c[1], 3.0f); EXPECT_EQ(c[1], 3.0f);
EXPECT_EQ(c[2], 4.0f); EXPECT_EQ(c[2], 4.0f);
@@ -84,58 +90,64 @@ TEST(Vec, ElementwiseAndScalarOps) {
} }
// Length, dot, cross, normalize // Length, dot, cross, normalize
TEST(Vec, MagnitudeAndDot) { TEST(Vec, MagnitudeAndDot)
Vec3 v{3.0f, 4.0f, 12.0f}; {
Vec3 v { 3.0f, 4.0f, 12.0f };
EXPECT_FLOAT_EQ(v.magnitude(), 13.0f); EXPECT_FLOAT_EQ(v.magnitude(), 13.0f);
EXPECT_FLOAT_EQ(v.length(), 13.0f); EXPECT_FLOAT_EQ(v.length(), 13.0f);
Vec3 u{1.0f, 0.0f, 2.0f}; Vec3 u { 1.0f, 0.0f, 2.0f };
EXPECT_FLOAT_EQ(v.dot(u), 27.0f); EXPECT_FLOAT_EQ(v.dot(u), 27.0f);
} }
TEST(Vec, Cross3D) { TEST(Vec, Cross3D)
Vec3 x{1.0f, 0.0f, 0.0f}; {
Vec3 y{0.0f, 1.0f, 0.0f}; Vec3 x { 1.0f, 0.0f, 0.0f };
Vec3 y { 0.0f, 1.0f, 0.0f };
auto z = x.cross(y); auto z = x.cross(y);
EXPECT_EQ(z[0], 0.0f); EXPECT_EQ(z[0], 0.0f);
EXPECT_EQ(z[1], 0.0f); EXPECT_EQ(z[1], 0.0f);
EXPECT_EQ(z[2], 1.0f); EXPECT_EQ(z[2], 1.0f);
} }
TEST(Vec, NormalizeAndSafeNormalize) { TEST(Vec, NormalizeAndSafeNormalize)
Vec3 v{10.0f, 0.0f, 0.0f}; {
Vec3 v { 10.0f, 0.0f, 0.0f };
auto n = v.normalized(); auto n = v.normalized();
auto ns = v.normalized_safe(); auto ns = v.normalized_safe();
ExpectVecNear(n, Vec3{1.0f, 0.0f, 0.0f}); ExpectVecNear(n, Vec3 { 1.0f, 0.0f, 0.0f });
Vec3 zero{}; Vec3 zero {};
auto zs = zero.normalized_safe(); auto zs = zero.normalized_safe();
EXPECT_EQ(zs[0], 0.0f); EXPECT_EQ(zs[0], 0.0f);
EXPECT_EQ(zs[1], 0.0f); EXPECT_EQ(zs[1], 0.0f);
EXPECT_EQ(zs[2], 0.0f); EXPECT_EQ(zs[2], 0.0f);
} }
TEST(Vec, DistanceAndProjection) { TEST(Vec, DistanceAndProjection)
Vec3 a{1.0f, 2.0f, 3.0f}; {
Vec3 b{4.0f, 6.0f, 3.0f}; Vec3 a { 1.0f, 2.0f, 3.0f };
Vec3 b { 4.0f, 6.0f, 3.0f };
EXPECT_FLOAT_EQ(a.distance(b), 5.0f); EXPECT_FLOAT_EQ(a.distance(b), 5.0f);
Vec3 n{2.0f, 0.0f, 0.0f}; // onto x-axis scaled Vec3 n { 2.0f, 0.0f, 0.0f }; // onto x-axis scaled
auto p = a.project_onto(n); // (a·n)/(n·n) * n = (2)/4 * n = 0.5 * n auto p = a.project_onto(n); // (a·n)/(n·n) * n = (2)/4 * n = 0.5 * n
ExpectVecNear(p, Vec3{1.0f, 0.0f, 0.0f}); ExpectVecNear(p, Vec3 { 1.0f, 0.0f, 0.0f });
} }
// Approx equal // Approx equal
TEST(Vec, ApproxEqual) { TEST(Vec, ApproxEqual)
Vec3 a{1.0f, 2.0f, 3.0f}; {
Vec3 b{1.0f + 1e-7f, 2.0f - 1e-7f, 3.0f}; Vec3 a { 1.0f, 2.0f, 3.0f };
Vec3 b { 1.0f + 1e-7f, 2.0f - 1e-7f, 3.0f };
EXPECT_TRUE(a.approx_equal(b, 1e-6f)); EXPECT_TRUE(a.approx_equal(b, 1e-6f));
EXPECT_FALSE(a.approx_equal(b, 1e-9f)); EXPECT_FALSE(a.approx_equal(b, 1e-9f));
} }
// std::get & tuple interop // std::get & tuple interop
TEST(Vec, StdGetAndTuple) { TEST(Vec, StdGetAndTuple)
Vec3 v{7.0f, 8.0f, 9.0f}; {
Vec3 v { 7.0f, 8.0f, 9.0f };
static_assert(std::tuple_size_v<Vec3> == 3); static_assert(std::tuple_size_v<Vec3> == 3);
static_assert(std::is_same_v<std::tuple_element_t<1, Vec3>, float>); static_assert(std::is_same_v<std::tuple_element_t<1, Vec3>, float>);
EXPECT_EQ(std::get<0>(v), 7.0f); EXPECT_EQ(std::get<0>(v), 7.0f);
@@ -144,8 +156,9 @@ TEST(Vec, StdGetAndTuple) {
} }
// Swizzle // Swizzle
TEST(Vec, SwizzleBasic) { TEST(Vec, SwizzleBasic)
const Vec3 v{1.0f, 2.0f, 3.0f}; {
const Vec3 v { 1.0f, 2.0f, 3.0f };
auto yz = smath::swizzle<"yz">(v); auto yz = smath::swizzle<"yz">(v);
EXPECT_EQ(yz[0], 2.0f); EXPECT_EQ(yz[0], 2.0f);
@@ -158,16 +171,18 @@ TEST(Vec, SwizzleBasic) {
} }
// std::formatter // std::formatter
TEST(Vec, Formatter) { TEST(Vec, Formatter)
smath::Vec<3, int> vi{1, 2, 3}; {
smath::Vec<3, int> vi { 1, 2, 3 };
std::string s = std::format("{}", vi); std::string s = std::format("{}", vi);
EXPECT_EQ(s, "{1, 2, 3}"); EXPECT_EQ(s, "{1, 2, 3}");
} }
// Conversions // Conversions
TEST(Vec, ExplicitConversionBetweenScalarTypes) { TEST(Vec, ExplicitConversionBetweenScalarTypes)
smath::Vec<3, int> vi{1, 2, 3}; {
smath::Vec<3, float> vf{vi}; smath::Vec<3, int> vi { 1, 2, 3 };
smath::Vec<3, float> vf { vi };
EXPECT_EQ(vf[0], 1.0f); EXPECT_EQ(vf[0], 1.0f);
EXPECT_EQ(vf[1], 2.0f); EXPECT_EQ(vf[1], 2.0f);
EXPECT_EQ(vf[2], 3.0f); EXPECT_EQ(vf[2], 3.0f);