mirror of
https://github.com/slendidev/smath.git
synced 2025-12-13 13:19:51 +02:00
Compare commits
4 Commits
a5d669235e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d3511a9b52 | |||
| eed719674f | |||
| 5f0badfe64 | |||
| 1a42238a41 |
29
.github/workflows/build.yml
vendored
Normal file
29
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Build project
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: git checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
- name: Building default package
|
||||
run: nix build .#default
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: result
|
||||
path: result
|
||||
@@ -67,6 +67,9 @@ endif()
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
|
||||
find_package(GTest QUIET)
|
||||
|
||||
if(NOT GTest_FOUND)
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
@@ -74,6 +77,7 @@ if(BUILD_TESTS)
|
||||
)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
endif()
|
||||
|
||||
file(GLOB TEST_SOURCES "${CMAKE_SOURCE_DIR}/tests/*.cpp")
|
||||
|
||||
@@ -86,3 +90,4 @@ if(BUILD_TESTS)
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(smath_tests)
|
||||
endif()
|
||||
|
||||
|
||||
17
README.md
17
README.md
@@ -1,4 +1,19 @@
|
||||
# smath
|
||||
|
||||
Single-file linear algebra math library for C++23.
|
||||
Single-file, header-only linear algebra math library for C++23.
|
||||
|
||||
## Features
|
||||
|
||||
- Generic `Vec<N, T>` class with useful aliases `Vec2/Vec3/Vec4` and friendly accessors (`x/y/z/w`, `r/g/b/a`). They support approx-equal and tuple/structured bindings.
|
||||
- `std::format` support.
|
||||
- Compile-time swizzles via `swizzle<"...">`.
|
||||
- Generic matrix `Mat` class with useful aliases `Mat2/Mat3/Mat4`.
|
||||
- `Quaternion<T>` built on `Vec4`.
|
||||
- Angle helpers `rad/deg/turns` respecting a configurable base unit via the macro `SMATH_ANGLE_UNIT`.
|
||||
- Optional implicit conversions.
|
||||
- Packing utilities for normalized RGBA (`pack_unorm4x8`, `unpack_snorm4x8`, etc.).
|
||||
|
||||
## License
|
||||
|
||||
This library is licensed under the Apache License 2.0. See the (LICENSE.txt)[LICENSE.txt] file for more details.
|
||||
|
||||
|
||||
15
flake.nix
15
flake.nix
@@ -36,7 +36,11 @@
|
||||
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [ pkgs.copyPkgconfigItems ];
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake
|
||||
gtest
|
||||
copyPkgconfigItems
|
||||
];
|
||||
|
||||
pkgconfigItems = [
|
||||
(pkgs.makePkgconfigItem rec {
|
||||
@@ -51,17 +55,18 @@
|
||||
})
|
||||
];
|
||||
|
||||
dontBuild = true;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out/include
|
||||
cp include/*.hpp $out/include/
|
||||
cp ../include/smath.hpp $out/include/
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
dontBuild = false;
|
||||
doCheck = true;
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = desc;
|
||||
description = "Single-file linear algebra math library for C++23.";
|
||||
homepage = "https://github.com/slendidev/smath";
|
||||
license = licenses.asl20;
|
||||
platforms = platforms.all;
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
@@ -36,15 +38,15 @@
|
||||
namespace smath {
|
||||
|
||||
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 {
|
||||
|
||||
#define SMATH_STR(x) #x
|
||||
#define SMATH_XSTR(x) SMATH_STR(x)
|
||||
|
||||
consteval bool streq(const char *a, const char *b) {
|
||||
consteval bool streq(const char *a, const char *b)
|
||||
{
|
||||
for (;; ++a, ++b) {
|
||||
if (*a != *b)
|
||||
return false;
|
||||
@@ -59,7 +61,8 @@ enum class AngularUnit {
|
||||
Turns,
|
||||
};
|
||||
|
||||
consteval std::optional<AngularUnit> parse_unit(const char *s) {
|
||||
consteval std::optional<AngularUnit> parse_unit(char const *s)
|
||||
{
|
||||
if (streq(s, "rad"))
|
||||
return AngularUnit::Radians;
|
||||
if (streq(s, "deg"))
|
||||
@@ -76,7 +79,8 @@ static_assert(SMATH_ANGLE_UNIT_ID != std::nullopt,
|
||||
template<std::size_t N> struct FixedString {
|
||||
char data[N] {};
|
||||
static constexpr std::size_t size = N - 1;
|
||||
constexpr FixedString(char const (&s)[N]) {
|
||||
constexpr FixedString(char const (&s)[N])
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
data[i] = s[i];
|
||||
}
|
||||
@@ -87,19 +91,63 @@ template <std::size_t M, class U> struct is_Vec<Vec<M, U>> : std::true_type {};
|
||||
template<class X>
|
||||
inline constexpr bool is_Vec_v = is_Vec<std::remove_cvref_t<X>>::value;
|
||||
template<class X>
|
||||
inline constexpr bool is_scalar_v =
|
||||
std::is_arithmetic_v<std::remove_cvref_t<X>>;
|
||||
inline constexpr bool is_scalar_v
|
||||
= std::is_arithmetic_v<std::remove_cvref_t<X>>;
|
||||
template<class X> struct Vec_size;
|
||||
template<std::size_t M, class U>
|
||||
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
|
||||
{
|
||||
static_assert(std::is_floating_point_v<T>);
|
||||
T c = std::clamp(v, T(0), T(1));
|
||||
T scaled = c * T(255);
|
||||
int i = static_cast<int>(scaled + T(0.5));
|
||||
if (i < 0)
|
||||
i = 0;
|
||||
if (i > 255)
|
||||
i = 255;
|
||||
return static_cast<std::uint8_t>(i);
|
||||
}
|
||||
|
||||
template<class T> constexpr auto pack_snorm8(T v) -> std::int8_t
|
||||
{
|
||||
static_assert(std::is_floating_point_v<T>);
|
||||
T c = std::clamp(v, T(-1), T(1));
|
||||
T scaled = c * T(127);
|
||||
int i
|
||||
= static_cast<int>(scaled >= T(0) ? scaled + T(0.5) : scaled - T(0.5));
|
||||
if (i < -127)
|
||||
i = -127;
|
||||
if (i > 127)
|
||||
i = 127;
|
||||
return static_cast<std::int8_t>(i);
|
||||
}
|
||||
|
||||
template<class T> constexpr auto unpack_unorm8(std::uint8_t b) -> T
|
||||
{
|
||||
static_assert(std::is_floating_point_v<T>);
|
||||
return static_cast<T>(b) / T(255);
|
||||
}
|
||||
|
||||
template<class T> constexpr auto unpack_snorm8(std::int8_t b) -> T
|
||||
{
|
||||
static_assert(std::is_floating_point_v<T>);
|
||||
int i = static_cast<int>(b);
|
||||
if (i < -127)
|
||||
i = -127;
|
||||
if (i > 127)
|
||||
i = 127;
|
||||
return static_cast<T>(i) / T(127);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
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:
|
||||
template <class X> static consteval std::size_t extent() {
|
||||
template<class X> static consteval std::size_t extent()
|
||||
{
|
||||
if constexpr (detail::is_Vec_v<X>)
|
||||
return detail::Vec_size<std::remove_cvref_t<X>>::value;
|
||||
else if constexpr (detail::is_scalar_v<X>)
|
||||
@@ -107,33 +155,38 @@ private:
|
||||
else
|
||||
return 0; // Should be unreachable
|
||||
}
|
||||
template <class... Args> static consteval std::size_t total_extent() {
|
||||
template<class... Args> static consteval std::size_t total_extent()
|
||||
{
|
||||
return (extent<Args>() + ... + 0);
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
constexpr Vec() noexcept {
|
||||
constexpr Vec() noexcept
|
||||
{
|
||||
for (auto &v : *this)
|
||||
v = T(0);
|
||||
}
|
||||
|
||||
explicit constexpr Vec(T const &s) noexcept {
|
||||
explicit constexpr Vec(T const &s) noexcept
|
||||
{
|
||||
for (auto &v : *this)
|
||||
v = s;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
requires((detail::is_scalar_v<Args> || detail::is_Vec_v<Args>) && ...) &&
|
||||
(total_extent<Args...>() == N) &&
|
||||
(!(sizeof...(Args) == 1 && (detail::is_Vec_v<Args> && ...)))
|
||||
constexpr Vec(Args &&...args) noexcept {
|
||||
requires((detail::is_scalar_v<Args> || detail::is_Vec_v<Args>) && ...)
|
||||
&& (total_extent<Args...>() == N)
|
||||
&& (!(sizeof...(Args) == 1 && (detail::is_Vec_v<Args> && ...)))
|
||||
constexpr Vec(Args &&...args) noexcept
|
||||
{
|
||||
std::size_t i = 0;
|
||||
(fill_one(i, std::forward<Args>(args)), ...);
|
||||
}
|
||||
|
||||
// Member accesses
|
||||
// NOTE: This can (probably) be improved with C++26 reflection in the future.
|
||||
// NOTE: This can (probably) be improved with C++26 reflection in the
|
||||
// future.
|
||||
#define VEC_ACC(component, req, idx) \
|
||||
constexpr auto component() noexcept -> T &requires(N >= req) { \
|
||||
return (*this)[idx]; \
|
||||
@@ -163,17 +216,20 @@ public:
|
||||
#undef VEC_ACC
|
||||
|
||||
template<class... Args, std::size_t... Is>
|
||||
constexpr void unpack_impl(std::index_sequence<Is...>,
|
||||
Args &...args) noexcept {
|
||||
constexpr void unpack_impl(
|
||||
std::index_sequence<Is...>, Args &...args) noexcept
|
||||
{
|
||||
((args = (*this)[Is]), ...);
|
||||
}
|
||||
|
||||
template <class... Args> constexpr void unpack(Args &...args) noexcept {
|
||||
template<class... Args> constexpr void unpack(Args &...args) noexcept
|
||||
{
|
||||
unpack_impl(std::index_sequence_for<Args...> {}, args...);
|
||||
}
|
||||
|
||||
// Unary
|
||||
constexpr auto operator-() noexcept -> Vec {
|
||||
constexpr auto operator-() noexcept -> Vec
|
||||
{
|
||||
Vec r {};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = -(*this)[i];
|
||||
@@ -181,16 +237,20 @@ public:
|
||||
}
|
||||
|
||||
// RHS operations
|
||||
friend constexpr auto operator+(T s, Vec const &v) noexcept -> Vec {
|
||||
friend constexpr auto operator+(T s, Vec const &v) noexcept -> Vec
|
||||
{
|
||||
return v + s;
|
||||
}
|
||||
friend constexpr auto operator-(T s, Vec const &v) noexcept -> Vec {
|
||||
friend constexpr auto operator-(T s, Vec const &v) noexcept -> Vec
|
||||
{
|
||||
return Vec(s) - v;
|
||||
}
|
||||
friend constexpr auto operator*(T s, Vec const &v) noexcept -> Vec {
|
||||
friend constexpr auto operator*(T s, Vec const &v) noexcept -> Vec
|
||||
{
|
||||
return v * s;
|
||||
}
|
||||
friend constexpr auto operator/(T s, Vec const &v) noexcept -> Vec {
|
||||
friend constexpr auto operator/(T s, Vec const &v) noexcept -> Vec
|
||||
{
|
||||
Vec r {};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = s / v[i];
|
||||
@@ -199,14 +259,16 @@ public:
|
||||
|
||||
// Members
|
||||
#define VEC_OP(op) \
|
||||
constexpr auto operator op(Vec const &rhs) const noexcept -> Vec { \
|
||||
constexpr auto operator op(Vec const &rhs) const noexcept -> Vec \
|
||||
{ \
|
||||
Vec result {}; \
|
||||
for (std::size_t i = 0; i < N; ++i) { \
|
||||
result[i] = (*this)[i] op rhs[i]; \
|
||||
} \
|
||||
return result; \
|
||||
} \
|
||||
constexpr auto operator op(T const &rhs) const noexcept -> Vec { \
|
||||
constexpr auto operator op(T const &rhs) const noexcept -> Vec \
|
||||
{ \
|
||||
Vec result {}; \
|
||||
for (std::size_t i = 0; i < N; ++i) { \
|
||||
result[i] = (*this)[i] op rhs; \
|
||||
@@ -219,12 +281,14 @@ public:
|
||||
VEC_OP(/)
|
||||
#undef VEC_OP
|
||||
#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) \
|
||||
(*this)[i] sym## = rhs[i]; \
|
||||
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) \
|
||||
(*this)[i] sym## = s; \
|
||||
return *this; \
|
||||
@@ -235,14 +299,16 @@ public:
|
||||
VEC_OP_ASSIGN(/)
|
||||
#undef VEC_OP_ASSIGN
|
||||
|
||||
constexpr auto operator==(Vec const &v) const noexcept -> bool {
|
||||
constexpr auto operator==(Vec const &v) const noexcept -> bool
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
if ((*this)[i] != v[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr auto operator!=(Vec const &v) const noexcept -> bool {
|
||||
constexpr auto operator!=(Vec const &v) const noexcept -> bool
|
||||
{
|
||||
return !(*this == v);
|
||||
}
|
||||
|
||||
@@ -262,13 +328,15 @@ public:
|
||||
|
||||
template<typename U = T>
|
||||
requires std::is_floating_point_v<U>
|
||||
constexpr auto normalized_safe(U eps = EPS_DEFAULT) const noexcept -> Vec {
|
||||
constexpr auto normalized_safe(U eps = EPS_DEFAULT) const noexcept -> Vec
|
||||
{
|
||||
auto m = magnitude();
|
||||
return (m > eps) ? (*this) / m : Vec {};
|
||||
}
|
||||
template<typename U = T>
|
||||
requires std::is_floating_point_v<U>
|
||||
constexpr auto normalize_safe(U eps = EPS_DEFAULT) const noexcept -> Vec {
|
||||
constexpr auto normalize_safe(U eps = EPS_DEFAULT) const noexcept -> Vec
|
||||
{
|
||||
return normalized_safe(eps);
|
||||
}
|
||||
|
||||
@@ -288,7 +356,8 @@ public:
|
||||
return this->normalized();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto dot(Vec<N, T> const &other) const noexcept -> T {
|
||||
[[nodiscard]] constexpr auto dot(Vec<N, T> const &other) const noexcept -> T
|
||||
{
|
||||
T res = 0;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
res += (*this)[i] * other[i];
|
||||
@@ -299,8 +368,9 @@ public:
|
||||
static constexpr T EPS_DEFAULT = T(1e-6);
|
||||
template<class U = T>
|
||||
requires std::is_floating_point_v<U>
|
||||
[[nodiscard]] constexpr auto
|
||||
approx_equal(Vec const &rhs, U eps = EPS_DEFAULT) const noexcept {
|
||||
[[nodiscard]] constexpr auto approx_equal(
|
||||
Vec const &rhs, U eps = EPS_DEFAULT) const noexcept
|
||||
{
|
||||
using F = std::conditional_t<std::is_floating_point_v<U>, U, double>;
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
if (std::abs(F((*this)[i] - rhs[i])) > F(eps))
|
||||
@@ -320,8 +390,8 @@ public:
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
requires(N == 3)
|
||||
constexpr auto cross(const Vec &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],
|
||||
(*this)[2] * r[0] - (*this)[0] * r[2],
|
||||
(*this)[0] * r[1] - (*this)[1] * r[0] };
|
||||
@@ -344,15 +414,16 @@ public:
|
||||
template<class U>
|
||||
requires(std::is_arithmetic_v<U> && N >= 1)
|
||||
constexpr explicit(!std::is_convertible_v<U, T>)
|
||||
Vec(Vec<N, U> const &other) noexcept {
|
||||
Vec(Vec<N, U> const &other) noexcept
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
this->operator[](i) = static_cast<T>(other[i]);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
requires(std::is_arithmetic_v<U> && N >= 1)
|
||||
constexpr explicit(!std::is_convertible_v<T, U>)
|
||||
operator Vec<N, U>() const noexcept {
|
||||
requires(std::is_arithmetic_v<U> && N >= 1) constexpr explicit(
|
||||
!std::is_convertible_v<T, U>) operator Vec<N, U>() const noexcept
|
||||
{
|
||||
Vec<N, U> r {};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = static_cast<U>((*this)[i]);
|
||||
@@ -361,51 +432,60 @@ public:
|
||||
|
||||
template<class U>
|
||||
requires(std::is_arithmetic_v<U> && !std::is_same_v<U, T>)
|
||||
constexpr auto operator=(Vec<N, U> const &rhs) noexcept -> Vec & {
|
||||
constexpr auto operator=(Vec<N, U> const &rhs) noexcept -> Vec &
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
(*this)[i] = static_cast<T>(rhs[i]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr void fill_one(std::size_t &i, const T &v) noexcept {
|
||||
constexpr void fill_one(std::size_t &i, T const &v) noexcept
|
||||
{
|
||||
(*this)[i++] = v;
|
||||
}
|
||||
#ifdef SMATH_IMPLICIT_CONVERSIONS
|
||||
template<class U>
|
||||
requires std::is_arithmetic_v<U> && (!std::is_same_v<U, T>)
|
||||
constexpr void fill_one(std::size_t &i, const U &v) noexcept {
|
||||
requires std::is_arithmetic_v<U>
|
||||
&& (!std::is_same_v<U, T>)constexpr void fill_one(
|
||||
std::size_t &i, const U &v) noexcept
|
||||
{
|
||||
(*this)[i++] = static_cast<T>(v);
|
||||
}
|
||||
template<std::size_t M, class U>
|
||||
constexpr void fill_one(std::size_t &i, const Vec<M, U> &v) noexcept {
|
||||
constexpr void fill_one(std::size_t &i, Vec<M, U> const &v) noexcept
|
||||
{
|
||||
for (std::size_t k = 0; k < M; ++k)
|
||||
(*this)[i++] = static_cast<T>(v[k]);
|
||||
}
|
||||
#endif // SMATH_IMPLICIT_CONVERSIONS
|
||||
template<std::size_t M>
|
||||
constexpr void fill_one(std::size_t &i, const Vec<M, T> &v) noexcept {
|
||||
constexpr void fill_one(std::size_t &i, const Vec<M, T> &v) noexcept
|
||||
{
|
||||
for (std::size_t k = 0; k < M; ++k)
|
||||
(*this)[i++] = static_cast<T>(v[k]);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t I, size_t N, class T> constexpr T &get(Vec<N, T> &v) noexcept {
|
||||
template<size_t I, size_t N, class T> constexpr T &get(Vec<N, T> &v) noexcept
|
||||
{
|
||||
static_assert(I < N);
|
||||
return v[I];
|
||||
}
|
||||
template<size_t I, size_t N, class T>
|
||||
constexpr const T &get(const Vec<N, T> &v) noexcept {
|
||||
constexpr T const &get(Vec<N, T> const &v) noexcept
|
||||
{
|
||||
static_assert(I < N);
|
||||
return v[I];
|
||||
}
|
||||
template <size_t I, size_t N, class T>
|
||||
constexpr T &&get(Vec<N, T> &&v) noexcept {
|
||||
template<size_t I, size_t N, class T> constexpr T &&get(Vec<N, T> &&v) noexcept
|
||||
{
|
||||
static_assert(I < N);
|
||||
return std::move(v[I]);
|
||||
}
|
||||
template<size_t I, size_t N, class T>
|
||||
constexpr const T &&get(const Vec<N, T> &&v) noexcept {
|
||||
constexpr T const &&get(Vec<N, T> const &&v) noexcept
|
||||
{
|
||||
static_assert(I < N);
|
||||
return std::move(v[I]);
|
||||
}
|
||||
@@ -416,7 +496,8 @@ using VecOrScalar = std::conditional_t<N == 1, T, Vec<N, T>>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
consteval auto char_to_idx(char c) -> std::size_t {
|
||||
consteval auto char_to_idx(char c) -> std::size_t
|
||||
{
|
||||
if (c == 'r' || c == 'x' || c == 's' || c == 'u')
|
||||
return 0;
|
||||
else if (c == 'g' || c == 'y' || c == 't' || c == 'v')
|
||||
@@ -428,7 +509,8 @@ consteval auto char_to_idx(char c) -> std::size_t {
|
||||
return static_cast<std::size_t>(-1);
|
||||
}
|
||||
|
||||
constexpr auto is_valid(char c) -> bool {
|
||||
constexpr auto is_valid(char c) -> bool
|
||||
{
|
||||
switch (c) {
|
||||
case 'r':
|
||||
case 'g':
|
||||
@@ -453,10 +535,11 @@ constexpr auto is_valid(char c) -> bool {
|
||||
|
||||
template<detail::FixedString S, std::size_t N, typename T, std::size_t... I>
|
||||
constexpr auto swizzle_impl(Vec<N, T> const &v, std::index_sequence<I...>)
|
||||
-> VecOrScalar<S.size, T> {
|
||||
-> VecOrScalar<S.size, T>
|
||||
{
|
||||
static_assert(((is_valid(S[I])) && ...), "Invalid swizzle component");
|
||||
static_assert(((char_to_idx(S[I]) < N) && ...),
|
||||
"Pattern index out of bounds");
|
||||
static_assert(
|
||||
((char_to_idx(S[I]) < N) && ...), "Pattern index out of bounds");
|
||||
VecOrScalar<S.size, T> out {};
|
||||
std::size_t i = 0;
|
||||
((out[i++] = v[char_to_idx(S[I])]), ...);
|
||||
@@ -474,14 +557,15 @@ concept SwizzleInBounds = []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
}(std::make_index_sequence<S.size> {});
|
||||
|
||||
template<FixedString S, std::size_t N>
|
||||
concept ValidSwizzle =
|
||||
(S.size > 0) && SwizzleCharsOK<S> && SwizzleInBounds<S, N>;
|
||||
concept ValidSwizzle
|
||||
= (S.size > 0) && SwizzleCharsOK<S> && SwizzleInBounds<S, N>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<detail::FixedString S, std::size_t N, typename T>
|
||||
requires detail::ValidSwizzle<S, N>
|
||||
constexpr auto swizzle(Vec<N, T> const &v) -> VecOrScalar<S.size, T> {
|
||||
constexpr auto swizzle(Vec<N, T> const &v) -> VecOrScalar<S.size, T>
|
||||
{
|
||||
return detail::swizzle_impl<S>(v, std::make_index_sequence<S.size> {});
|
||||
}
|
||||
|
||||
@@ -493,38 +577,41 @@ using Vec2d = Vec<2, double>;
|
||||
using Vec3d = Vec<3, double>;
|
||||
using Vec4d = Vec<4, double>;
|
||||
|
||||
template <class T> constexpr auto deg(T const value) -> T {
|
||||
template<class T> constexpr auto deg(T const value) -> T
|
||||
{
|
||||
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
|
||||
return value;
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
|
||||
detail::AngularUnit::Radians) {
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Radians) {
|
||||
return value * static_cast<T>(std::numbers::pi / 180.0);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
|
||||
detail::AngularUnit::Turns) {
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Turns) {
|
||||
return value / static_cast<T>(360.0);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> constexpr auto rad(T const value) -> T {
|
||||
template<class T> constexpr auto rad(T const value) -> T
|
||||
{
|
||||
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
|
||||
return value * static_cast<T>(180.0 / std::numbers::pi);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
|
||||
detail::AngularUnit::Radians) {
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Radians) {
|
||||
return value;
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
|
||||
detail::AngularUnit::Turns) {
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Turns) {
|
||||
return value / (static_cast<T>(2.0) * static_cast<T>(std::numbers::pi));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> constexpr auto turns(T const value) -> T {
|
||||
template<class T> constexpr auto turns(T const value) -> T
|
||||
{
|
||||
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
|
||||
return value * static_cast<T>(360.0);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
|
||||
detail::AngularUnit::Radians) {
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Radians) {
|
||||
return value * (static_cast<T>(2.0) * static_cast<T>(std::numbers::pi));
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID ==
|
||||
detail::AngularUnit::Turns) {
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Turns) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -542,39 +629,100 @@ template <class T> struct Quaternion : Vec<4, T> {
|
||||
constexpr T &z() noexcept { return Base::z(); }
|
||||
constexpr T &w() noexcept { return Base::w(); }
|
||||
|
||||
constexpr auto operator*(Quaternion const &rhs) const noexcept -> Quaternion {
|
||||
constexpr auto operator*(Quaternion const &rhs) const noexcept -> Quaternion
|
||||
{
|
||||
Quaternion r;
|
||||
auto const &a = *this;
|
||||
|
||||
r.x() =
|
||||
a.w() * rhs.x() + a.x() * rhs.w() + a.y() * rhs.z() - a.z() * rhs.y();
|
||||
r.y() =
|
||||
a.w() * rhs.y() - a.x() * rhs.z() + a.y() * rhs.w() + a.z() * rhs.x();
|
||||
r.z() =
|
||||
a.w() * rhs.z() + a.x() * rhs.y() - a.y() * rhs.x() + a.z() * rhs.w();
|
||||
r.w() =
|
||||
a.w() * rhs.w() - a.x() * rhs.x() - a.y() * rhs.y() - a.z() * rhs.z();
|
||||
r.x() = a.w() * rhs.x() + a.x() * rhs.w() + a.y() * rhs.z()
|
||||
- a.z() * rhs.y();
|
||||
r.y() = a.w() * rhs.y() - a.x() * rhs.z() + a.y() * rhs.w()
|
||||
+ a.z() * rhs.x();
|
||||
r.z() = a.w() * rhs.z() + a.x() * rhs.y() - a.y() * rhs.x()
|
||||
+ a.z() * rhs.w();
|
||||
r.w() = a.w() * rhs.w() - a.x() * rhs.x() - a.y() * rhs.y()
|
||||
- a.z() * rhs.z();
|
||||
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
requires std::is_floating_point_v<T>
|
||||
[[nodiscard]] constexpr auto pack_unorm4x8(Vec<4, T> const &v) -> std::uint32_t
|
||||
{
|
||||
std::uint32_t r = detail::pack_unorm8(v[0]);
|
||||
std::uint32_t g = detail::pack_unorm8(v[1]);
|
||||
std::uint32_t b = detail::pack_unorm8(v[2]);
|
||||
std::uint32_t a = detail::pack_unorm8(v[3]);
|
||||
|
||||
return (r) | (g << 8) | (b << 16) | (a << 24);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
requires std::is_floating_point_v<T>
|
||||
[[nodiscard]] constexpr auto pack_snorm4x8(Vec<4, T> const &v) -> std::uint32_t
|
||||
{
|
||||
std::uint32_t r = static_cast<std::uint8_t>(detail::pack_snorm8(v[0]));
|
||||
std::uint32_t g = static_cast<std::uint8_t>(detail::pack_snorm8(v[1]));
|
||||
std::uint32_t b = static_cast<std::uint8_t>(detail::pack_snorm8(v[2]));
|
||||
std::uint32_t a = static_cast<std::uint8_t>(detail::pack_snorm8(v[3]));
|
||||
|
||||
return (r) | (g << 8) | (b << 16) | (a << 24);
|
||||
}
|
||||
|
||||
template<class T = float>
|
||||
requires std::is_floating_point_v<T>
|
||||
[[nodiscard]] constexpr auto unpack_unorm4x8(std::uint32_t packed) -> Vec<4, T>
|
||||
{
|
||||
std::uint8_t r = static_cast<std::uint8_t>(packed & 0xFFu);
|
||||
std::uint8_t g = static_cast<std::uint8_t>((packed >> 8) & 0xFFu);
|
||||
std::uint8_t b = static_cast<std::uint8_t>((packed >> 16) & 0xFFu);
|
||||
std::uint8_t a = static_cast<std::uint8_t>((packed >> 24) & 0xFFu);
|
||||
|
||||
return {
|
||||
detail::unpack_unorm8<T>(r),
|
||||
detail::unpack_unorm8<T>(g),
|
||||
detail::unpack_unorm8<T>(b),
|
||||
detail::unpack_unorm8<T>(a),
|
||||
};
|
||||
}
|
||||
|
||||
template<class T = float>
|
||||
requires std::is_floating_point_v<T>
|
||||
[[nodiscard]] constexpr auto unpack_snorm4x8(std::uint32_t packed) -> Vec<4, T>
|
||||
{
|
||||
std::int8_t r = static_cast<std::int8_t>(packed & 0xFFu);
|
||||
std::int8_t g = static_cast<std::int8_t>((packed >> 8) & 0xFFu);
|
||||
std::int8_t b = static_cast<std::int8_t>((packed >> 16) & 0xFFu);
|
||||
std::int8_t a = static_cast<std::int8_t>((packed >> 24) & 0xFFu);
|
||||
|
||||
return {
|
||||
detail::unpack_snorm8<T>(r),
|
||||
detail::unpack_snorm8<T>(g),
|
||||
detail::unpack_snorm8<T>(b),
|
||||
detail::unpack_snorm8<T>(a),
|
||||
};
|
||||
}
|
||||
|
||||
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::operator[];
|
||||
|
||||
constexpr auto operator[](std::size_t const row, std::size_t const column)
|
||||
-> T & {
|
||||
-> T &
|
||||
{
|
||||
return col(column)[row];
|
||||
}
|
||||
constexpr auto operator[](std::size_t const row,
|
||||
std::size_t const column) const -> T const & {
|
||||
constexpr auto operator[](
|
||||
std::size_t const row, std::size_t const column) const -> T const &
|
||||
{
|
||||
return col(column)[row];
|
||||
}
|
||||
|
||||
constexpr Mat() noexcept {
|
||||
constexpr Mat() noexcept
|
||||
{
|
||||
for (auto &col : *this)
|
||||
col = Vec<R, T> {};
|
||||
}
|
||||
@@ -589,99 +737,119 @@ struct Mat : std::array<Vec<R, T>, C> {
|
||||
}
|
||||
|
||||
template<typename... Cols>
|
||||
requires(sizeof...(Cols) == C &&
|
||||
(std::same_as<std::remove_cvref_t<Cols>, Vec<R, T>> && ...))
|
||||
constexpr Mat(Cols const &...cols) noexcept : Base{cols...} {}
|
||||
requires(sizeof...(Cols) == C
|
||||
&& (std::same_as<std::remove_cvref_t<Cols>, Vec<R, T>> && ...))
|
||||
constexpr Mat(Cols const &...cols) noexcept
|
||||
: Base { cols... }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr auto col(std::size_t j) noexcept -> Vec<R, T> & {
|
||||
constexpr auto col(std::size_t j) noexcept -> Vec<R, T> &
|
||||
{
|
||||
return (*this)[j];
|
||||
}
|
||||
constexpr auto col(std::size_t j) const noexcept -> Vec<R, T> const & {
|
||||
constexpr auto col(std::size_t j) const noexcept -> Vec<R, T> const &
|
||||
{
|
||||
return (*this)[j];
|
||||
}
|
||||
|
||||
constexpr auto operator()(std::size_t row, std::size_t col) noexcept -> T & {
|
||||
constexpr auto operator()(std::size_t row, std::size_t col) noexcept -> T &
|
||||
{
|
||||
return (*this)[col][row];
|
||||
}
|
||||
constexpr auto operator()(std::size_t row, std::size_t col) const noexcept
|
||||
-> T const & {
|
||||
-> T const &
|
||||
{
|
||||
return (*this)[col][row];
|
||||
}
|
||||
|
||||
constexpr auto operator-() const noexcept -> Mat {
|
||||
constexpr auto operator-() const noexcept -> Mat
|
||||
{
|
||||
Mat r {};
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
r[c] = -(*this)[c];
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr auto operator+=(Mat const &rhs) noexcept -> Mat & {
|
||||
constexpr auto operator+=(Mat const &rhs) noexcept -> Mat &
|
||||
{
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
(*this)[c] += rhs[c];
|
||||
return *this;
|
||||
}
|
||||
constexpr auto operator-=(Mat const &rhs) noexcept -> Mat & {
|
||||
constexpr auto operator-=(Mat const &rhs) noexcept -> Mat &
|
||||
{
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
(*this)[c] -= rhs[c];
|
||||
return *this;
|
||||
}
|
||||
friend constexpr auto operator+(Mat lhs, Mat const &rhs) noexcept -> Mat {
|
||||
friend constexpr auto operator+(Mat lhs, Mat const &rhs) noexcept -> Mat
|
||||
{
|
||||
lhs += rhs;
|
||||
return lhs;
|
||||
}
|
||||
friend constexpr auto operator-(Mat lhs, Mat const &rhs) noexcept -> Mat {
|
||||
friend constexpr auto operator-(Mat lhs, Mat const &rhs) noexcept -> Mat
|
||||
{
|
||||
lhs -= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
constexpr auto operator*=(T const &s) noexcept -> Mat & {
|
||||
constexpr auto operator*=(T const &s) noexcept -> Mat &
|
||||
{
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
(*this)[c] *= s;
|
||||
return *this;
|
||||
}
|
||||
constexpr auto operator/=(T const &s) noexcept -> Mat & {
|
||||
constexpr auto operator/=(T const &s) noexcept -> Mat &
|
||||
{
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
(*this)[c] /= s;
|
||||
return *this;
|
||||
}
|
||||
friend constexpr auto operator*(Mat lhs, T const &s) noexcept -> Mat {
|
||||
friend constexpr auto operator*(Mat lhs, T const &s) noexcept -> Mat
|
||||
{
|
||||
lhs *= s;
|
||||
return lhs;
|
||||
}
|
||||
friend constexpr auto operator*(T const &s, Mat rhs) noexcept -> Mat {
|
||||
friend constexpr auto operator*(T const &s, Mat rhs) noexcept -> Mat
|
||||
{
|
||||
rhs *= s;
|
||||
return rhs;
|
||||
}
|
||||
friend constexpr auto operator/(Mat lhs, T const &s) noexcept -> Mat {
|
||||
friend constexpr auto operator/(Mat lhs, T const &s) noexcept -> Mat
|
||||
{
|
||||
lhs /= s;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto operator==(Mat const &rhs) const noexcept
|
||||
-> bool {
|
||||
-> bool
|
||||
{
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
if (!((*this)[c] == rhs[c]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
[[nodiscard]] constexpr auto operator!=(Mat const &rhs) const noexcept
|
||||
-> bool {
|
||||
-> bool
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
static constexpr T EPS_DEFAULT = T(1e-6);
|
||||
template<class U = T>
|
||||
requires std::is_floating_point_v<U>
|
||||
[[nodiscard]] constexpr auto approx_equal(Mat const &rhs,
|
||||
U eps = EPS_DEFAULT) const noexcept
|
||||
-> bool {
|
||||
[[nodiscard]] constexpr auto approx_equal(
|
||||
Mat const &rhs, U eps = EPS_DEFAULT) const noexcept -> bool
|
||||
{
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
if (!(*this)[c].approx_equal(rhs[c], eps))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto transposed() const noexcept -> Mat<C, R, T> {
|
||||
[[nodiscard]] constexpr auto transposed() const noexcept -> Mat<C, R, T>
|
||||
{
|
||||
Mat<C, R, T> r {};
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
for (std::size_t r_idx = 0; r_idx < R; ++r_idx)
|
||||
@@ -708,8 +876,9 @@ using Mat3d = Mat<3, 3, double>;
|
||||
using Mat4d = Mat<4, 4, double>;
|
||||
|
||||
template<std::size_t R, std::size_t C, typename T>
|
||||
[[nodiscard]] constexpr Vec<R, T> operator*(Mat<R, C, T> const &m,
|
||||
Vec<C, T> const &v) noexcept {
|
||||
[[nodiscard]] constexpr Vec<R, T> operator*(
|
||||
Mat<R, C, T> const &m, Vec<C, T> const &v) noexcept
|
||||
{
|
||||
Vec<R, T> out {};
|
||||
for (std::size_t c = 0; c < C; ++c)
|
||||
out += m.col(c) * v[c];
|
||||
@@ -718,8 +887,9 @@ template <std::size_t R, std::size_t C, typename T>
|
||||
|
||||
// Matrix * Matrix
|
||||
template<std::size_t R, std::size_t C, std::size_t K, typename T>
|
||||
[[nodiscard]] constexpr Mat<R, K, T> operator*(Mat<R, C, T> const &a,
|
||||
Mat<C, K, T> const &b) noexcept {
|
||||
[[nodiscard]] constexpr Mat<R, K, T> operator*(
|
||||
Mat<R, C, T> const &a, Mat<C, K, T> const &b) noexcept
|
||||
{
|
||||
Mat<R, K, T> out {};
|
||||
for (std::size_t k = 0; k < K; ++k) {
|
||||
for (std::size_t r = 0; r < R; ++r) {
|
||||
@@ -735,14 +905,16 @@ template <std::size_t R, std::size_t C, std::size_t K, typename T>
|
||||
// Mat3 transformations
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto translate(Mat<3, 3, T> const &m, Vec<2, T> const &v)
|
||||
-> Mat<3, 3, T> {
|
||||
-> Mat<3, 3, T>
|
||||
{
|
||||
Mat<3, 3, T> res { m };
|
||||
res[2] = m[0] * v[0] + m[1] * v[1] + m[2];
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto translate(Vec<2, T> const &v) -> Mat<3, 3, T> {
|
||||
[[nodiscard]] inline auto translate(Vec<2, T> const &v) -> Mat<3, 3, T>
|
||||
{
|
||||
Mat<3, 3, T> res { 1 };
|
||||
res[2].x() = v.x();
|
||||
res[2].y() = v.y();
|
||||
@@ -751,7 +923,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto rotate(Mat<3, 3, T> const &m, T const angle)
|
||||
-> Mat<3, 3, T> {
|
||||
-> Mat<3, 3, T>
|
||||
{
|
||||
Mat<3, 3, T> res;
|
||||
|
||||
T const c { std::cos(angle) };
|
||||
@@ -766,7 +939,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto scale(Mat<3, 3, T> const &m, Vec<2, T> const &v)
|
||||
-> Mat<3, 3, T> {
|
||||
-> Mat<3, 3, T>
|
||||
{
|
||||
Mat<3, 3, T> res;
|
||||
|
||||
res[0] = m[0] * v[0];
|
||||
@@ -778,7 +952,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto shear_x(Mat<3, 3, T> const &m, T const v)
|
||||
-> Mat<3, 3, T> {
|
||||
-> Mat<3, 3, T>
|
||||
{
|
||||
Mat<3, 3, T> res { 1 };
|
||||
res[1][0] = v;
|
||||
return m * res;
|
||||
@@ -786,7 +961,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto shear_y(Mat<3, 3, T> const &m, T const v)
|
||||
-> Mat<3, 3, T> {
|
||||
-> Mat<3, 3, T>
|
||||
{
|
||||
Mat<3, 3, T> res { 1 };
|
||||
res[0][1] = v;
|
||||
return m * res;
|
||||
@@ -795,15 +971,16 @@ template <typename T>
|
||||
// Mat4 transformations
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto translate(Mat<4, 4, T> const &m, Vec<3, T> const &v)
|
||||
-> Mat<4, 4, T> {
|
||||
-> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> res { m };
|
||||
res[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto translate(Vec<3, T> const &v) -> Mat<4, 4, T> {
|
||||
[[nodiscard]] inline auto translate(Vec<3, T> const &v) -> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> res { 1 };
|
||||
res[3].x() = v.x();
|
||||
res[3].y() = v.y();
|
||||
@@ -813,7 +990,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto rotate(Mat<4, 4, T> const &m, T const angle)
|
||||
-> Mat<4, 4, T> {
|
||||
-> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> res;
|
||||
|
||||
T const c { std::cos(angle) };
|
||||
@@ -829,7 +1007,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto scale(Mat<4, 4, T> const &m, Vec<3, T> const &v)
|
||||
-> Mat<4, 4, T> {
|
||||
-> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> res;
|
||||
|
||||
res[0] = m[0] * v[0];
|
||||
@@ -842,7 +1021,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto shear_x(Mat<4, 4, T> const &m, T const v)
|
||||
-> Mat<4, 4, T> {
|
||||
-> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> res { 1 };
|
||||
res[0, 1] = v;
|
||||
return m * res;
|
||||
@@ -850,7 +1030,8 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto shear_y(Mat<4, 4, T> const &m, T const v)
|
||||
-> Mat<4, 4, T> {
|
||||
-> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> res { 1 };
|
||||
res[1, 0] = v;
|
||||
return m * res;
|
||||
@@ -858,17 +1039,18 @@ template <typename T>
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto shear_z(Mat<4, 4, T> const &m, T const v)
|
||||
-> Mat<4, 4, T> {
|
||||
-> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> res { 1 };
|
||||
res[2, 0] = v;
|
||||
return m * res;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto
|
||||
matrix_ortho3d(T const left, 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> {
|
||||
[[nodiscard]] inline auto matrix_ortho3d(T const left, 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>
|
||||
{
|
||||
Mat<4, 4, T> res {};
|
||||
|
||||
res[0, 0] = 2 / (right - left);
|
||||
@@ -887,8 +1069,9 @@ matrix_ortho3d(T const left, T const right, T const bottom, T const top,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline auto matrix_perspective(T fovy, T aspect, T znear, T zfar,
|
||||
bool flip_z_axis = false) -> Mat<4, 4, T> {
|
||||
inline auto matrix_perspective(
|
||||
T fovy, T aspect, T znear, T zfar, bool flip_z_axis = false) -> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> m {};
|
||||
|
||||
T const f { T(1) / std::tan(fovy / T(2)) };
|
||||
@@ -912,9 +1095,10 @@ inline auto matrix_perspective(T fovy, T aspect, T znear, T zfar,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto
|
||||
matrix_look_at(Vec<3, T> const eye, Vec<3, T> const center, Vec<3, T> const up,
|
||||
bool flip_z_axis = false) -> Mat<4, 4, T> {
|
||||
[[nodiscard]] inline auto matrix_look_at(Vec<3, T> const eye,
|
||||
Vec<3, T> const center, Vec<3, T> const up, bool flip_z_axis = false)
|
||||
-> Mat<4, 4, T>
|
||||
{
|
||||
auto f = (center - eye).normalized();
|
||||
auto s = f.cross(up).normalized();
|
||||
auto u = s.cross(f);
|
||||
@@ -937,9 +1121,9 @@ matrix_look_at(Vec<3, T> const eye, Vec<3, T> const center, Vec<3, T> const up,
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] inline auto
|
||||
matrix_infinite_perspective(T const fovy, T const aspect, T const znear,
|
||||
bool flip_z_axis = false) -> Mat<4, 4, T> {
|
||||
[[nodiscard]] inline auto matrix_infinite_perspective(T const fovy,
|
||||
T const aspect, T const znear, bool flip_z_axis = false) -> Mat<4, 4, T>
|
||||
{
|
||||
Mat<4, 4, T> m {};
|
||||
|
||||
T const f = 1 / std::tan(fovy / T(2));
|
||||
@@ -964,12 +1148,14 @@ matrix_infinite_perspective(T const fovy, T const aspect, T const znear,
|
||||
template<std::size_t N, typename T>
|
||||
requires std::formattable<T, char>
|
||||
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);
|
||||
}
|
||||
|
||||
template<typename Ctx>
|
||||
auto format(smath::Vec<N, T> const &v, Ctx &ctx) const {
|
||||
auto format(smath::Vec<N, T> const &v, Ctx &ctx) const
|
||||
{
|
||||
auto out = ctx.out();
|
||||
*out++ = '{';
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
|
||||
Reference in New Issue
Block a user