mirror of
https://github.com/slendidev/smath.git
synced 2026-03-17 10:36:50 +02:00
Compare commits
5 Commits
830c64b25a
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b9adb0ddc | |||
| 889a42f9b5 | |||
| 8dc5ef8f24 | |||
| 0b685eb30b | |||
| aa8e3b1637 |
45
README.md
45
README.md
@@ -1,6 +1,43 @@
|
||||
# smath
|
||||
<p align="center">
|
||||
<img alt="smath logo" src="https://github.com/user-attachments/assets/d6f2ef4b-eca0-4004-9099-37423528bcba" />
|
||||
<br><br>
|
||||
<a href="https://github.com/slendidev/smath/actions/workflows/build.yml">
|
||||
<img src="https://github.com/slendidev/smath/actions/workflows/build.yml/badge.svg" alt="Build">
|
||||
</a>
|
||||
<a href="LICENSE.txt">
|
||||
<img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License: Apache-2.0">
|
||||
</a>
|
||||
<a href="https://en.cppreference.com/w/cpp/23">
|
||||
<img src="https://img.shields.io/badge/C%2B%2B-23-00599C.svg" alt="C++23">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Single-file, header-only linear algebra math library for C++23.
|
||||
## Quick Start
|
||||
|
||||
Create `main.cpp`:
|
||||
|
||||
```cpp
|
||||
#include <print>
|
||||
#include <smath.hpp>
|
||||
|
||||
int main() {
|
||||
using namespace smath;
|
||||
|
||||
Vec3 a{1.0f, 2.0f, 3.0f};
|
||||
Vec3 b{3.0f, 2.0f, 1.0f};
|
||||
|
||||
std::println("a + b = {}", a + b);
|
||||
std::println("dot(a, b) = {}", a.dot(b));
|
||||
std::println("normalized(a) = {}", a.normalized());
|
||||
}
|
||||
```
|
||||
|
||||
Build and run:
|
||||
|
||||
```bash
|
||||
g++ -std=c++23 -Iinclude main.cpp -o quickstart
|
||||
./quickstart
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
@@ -12,8 +49,8 @@ Single-file, header-only linear algebra math library for C++23.
|
||||
- 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.).
|
||||
- C++20 modules support.
|
||||
|
||||
## License
|
||||
|
||||
This library is licensed under the Apache License 2.0. See the (LICENSE.txt)[LICENSE.txt] file for more details.
|
||||
|
||||
This library is licensed under the Apache License 2.0. See the [LICENSE.txt](LICENSE.txt) file for more details.
|
||||
|
||||
@@ -188,9 +188,12 @@ public:
|
||||
// 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) { \
|
||||
constexpr auto component() noexcept -> T & \
|
||||
requires(N >= req) \
|
||||
{ \
|
||||
return (*this)[idx]; \
|
||||
} constexpr auto component() const->T const & \
|
||||
} \
|
||||
constexpr auto component() const -> T const & \
|
||||
requires(N >= req) \
|
||||
{ \
|
||||
return (*this)[idx]; \
|
||||
@@ -281,13 +284,13 @@ 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; \
|
||||
@@ -421,8 +424,9 @@ public:
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -446,9 +450,8 @@ private:
|
||||
}
|
||||
#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);
|
||||
}
|
||||
@@ -577,49 +580,54 @@ using Vec2d = Vec<2, double>;
|
||||
using Vec3d = Vec<3, double>;
|
||||
using Vec4d = Vec<4, double>;
|
||||
|
||||
template<class T> constexpr auto deg(T const value)
|
||||
template<class T>
|
||||
using angle_ret_t = std::conditional_t<std::is_same_v<T, float>, float, double>;
|
||||
|
||||
template<class T> constexpr auto deg(T value)
|
||||
{
|
||||
using R = std::common_type_t<T, double>;
|
||||
using R = angle_ret_t<T>;
|
||||
|
||||
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
|
||||
return static_cast<R>(value);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Radians) {
|
||||
return static_cast<R>(value) * static_cast<R>(std::numbers::pi / 180.0);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Turns) {
|
||||
} else {
|
||||
return static_cast<R>(value) / static_cast<R>(360.0);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> constexpr auto rad(T const value)
|
||||
template<class T> constexpr auto rad(T value)
|
||||
{
|
||||
using R = std::common_type_t<T, double>;
|
||||
using R = angle_ret_t<T>;
|
||||
|
||||
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
|
||||
return static_cast<R>(value) * static_cast<R>(180.0 / std::numbers::pi);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Radians) {
|
||||
return static_cast<R>(value);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Turns) {
|
||||
} else {
|
||||
return static_cast<R>(value)
|
||||
/ (static_cast<R>(2.0) * static_cast<R>(std::numbers::pi));
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> constexpr auto turns(T const value)
|
||||
template<class T> constexpr auto turns(T value)
|
||||
{
|
||||
using R = std::common_type_t<T, double>;
|
||||
using R = angle_ret_t<T>;
|
||||
|
||||
if constexpr (detail::SMATH_ANGLE_UNIT_ID == detail::AngularUnit::Degrees) {
|
||||
return static_cast<R>(value) * static_cast<R>(360.0);
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Radians) {
|
||||
return static_cast<R>(value)
|
||||
* (static_cast<R>(2.0) * static_cast<R>(std::numbers::pi));
|
||||
} else if constexpr (detail::SMATH_ANGLE_UNIT_ID
|
||||
== detail::AngularUnit::Turns) {
|
||||
} else {
|
||||
return static_cast<R>(value);
|
||||
}
|
||||
}
|
||||
template<std::size_t R, std::size_t C, typename T>
|
||||
requires std::is_arithmetic_v<T> struct Mat;
|
||||
|
||||
template<class T> struct Quaternion : Vec<4, T> {
|
||||
using Base = Vec<4, T>;
|
||||
@@ -654,6 +662,42 @@ template<class T> struct Quaternion : Vec<4, T> {
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto as_matrix() const noexcept -> Mat<4, 4, T>
|
||||
{
|
||||
auto const xx = x() * x();
|
||||
auto const yy = y() * y();
|
||||
auto const zz = z() * z();
|
||||
auto const xy = x() * y();
|
||||
auto const xz = x() * z();
|
||||
auto const yz = y() * z();
|
||||
auto const wx = w() * x();
|
||||
auto const wy = w() * y();
|
||||
auto const wz = w() * z();
|
||||
|
||||
return Mat<4, 4, T> {
|
||||
Vec<4, T> { 1 - 2 * (yy + zz), 2 * (xy + wz), 2 * (xz - wy), 0 },
|
||||
Vec<4, T> { 2 * (xy - wz), 1 - 2 * (xx + zz), 2 * (yz + wx), 0 },
|
||||
Vec<4, T> { 2 * (xz + wy), 2 * (yz - wx), 1 - 2 * (xx + yy), 0 },
|
||||
Vec<4, T> { 0, 0, 0, 1 },
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr auto from_axis_angle(
|
||||
Vec<3, T> const &axis, T const angle) noexcept -> Quaternion
|
||||
{
|
||||
auto const normalized_axis { axis.normalized_safe() };
|
||||
auto const half_angle { angle / static_cast<T>(2) };
|
||||
auto const sine { std::sin(half_angle) };
|
||||
auto const cosine { std::cos(half_angle) };
|
||||
|
||||
return Quaternion {
|
||||
normalized_axis.x() * sine,
|
||||
normalized_axis.y() * sine,
|
||||
normalized_axis.z() * sine,
|
||||
cosine,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
@@ -1105,28 +1149,18 @@ inline auto matrix_perspective(
|
||||
|
||||
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>
|
||||
Vec<3, T> const center, Vec<3, T> const up) -> Mat<4, 4, T>
|
||||
{
|
||||
auto f = (center - eye).normalized();
|
||||
auto s = f.cross(up).normalized();
|
||||
auto f = (center - eye).normalized_safe();
|
||||
auto s = f.cross(up).normalized_safe();
|
||||
auto u = s.cross(f);
|
||||
|
||||
if (!flip_z_axis) {
|
||||
return Mat<4, 4, T> {
|
||||
Vec<4, T> { s.x(), s.y(), s.z(), 0 },
|
||||
Vec<4, T> { u.x(), u.y(), u.z(), 0 },
|
||||
Vec<4, T> { -f.x(), -f.y(), -f.z(), 0 },
|
||||
Vec<4, T> { s.x(), u.x(), -f.x(), 0 },
|
||||
Vec<4, T> { s.y(), u.y(), -f.y(), 0 },
|
||||
Vec<4, T> { s.z(), u.z(), -f.z(), 0 },
|
||||
Vec<4, T> { -s.dot(eye), -u.dot(eye), f.dot(eye), 1 },
|
||||
};
|
||||
} else {
|
||||
return Mat<4, 4, T> {
|
||||
Vec<4, T> { s.x(), s.y(), s.z(), 0 },
|
||||
Vec<4, T> { u.x(), u.y(), u.z(), 0 },
|
||||
Vec<4, T> { f.x(), f.y(), f.z(), 0 },
|
||||
Vec<4, T> { -s.dot(eye), -u.dot(eye), -f.dot(eye), 1 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
Reference in New Issue
Block a user