mirror of
https://github.com/slendidev/smath.git
synced 2025-09-03 18:53:32 +03:00
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
[Bb]uild*
|
||||
.cache
|
19
CMakeLists.txt
Normal file
19
CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(SmathExamples CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_library(smath INTERFACE)
|
||||
target_include_directories(smath INTERFACE ${CMAKE_SOURCE_DIR}/include)
|
||||
add_library(smath::smath ALIAS smath)
|
||||
|
||||
option(BUILD_EXAMPLES "Build example programs" ON)
|
||||
if(BUILD_EXAMPLES)
|
||||
file(GLOB EXAMPLE_SOURCES "${CMAKE_SOURCE_DIR}/examples/*.cpp")
|
||||
foreach(EXAMPLE_FILE ${EXAMPLE_SOURCES})
|
||||
get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE)
|
||||
add_executable(${EXAMPLE_NAME} ${EXAMPLE_FILE})
|
||||
target_link_libraries(${EXAMPLE_NAME} PRIVATE smath::smath)
|
||||
endforeach()
|
||||
endif()
|
202
LICENSE.txt
Normal file
202
LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
4
README.md
Normal file
4
README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# smath
|
||||
|
||||
Single-file linear algebra math library for C++23.
|
||||
|
66
examples/tour.cpp
Normal file
66
examples/tour.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Copyright 2025 Slendi <slendi@socopon.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <print>
|
||||
|
||||
// #define SMATH_IMPLICIT_CONVERSIONS
|
||||
|
||||
#include <smath.hpp>
|
||||
|
||||
int main() {
|
||||
using namespace smath;
|
||||
Vec3 v{1, 2, 3};
|
||||
std::println("v: {}", v);
|
||||
auto v2 = swizzle<"zyx">(v);
|
||||
std::println("v2: {}", v2);
|
||||
std::println("+: {}", v + v2);
|
||||
std::println("-: {}", v - v2);
|
||||
std::println("*: {}", v * v2);
|
||||
std::println("/: {}", v / v2);
|
||||
std::println("dot: {}", v.dot(v2));
|
||||
std::println("rrggbb: {}", swizzle<"rrggbb">(v));
|
||||
std::println("Magnitude: {}", v.magnitude());
|
||||
std::println("Normalized: {}", v.normalized());
|
||||
std::println("(alias) Unit: {}", v.unit());
|
||||
std::println("(alias) Normalize: {}", v.normalize());
|
||||
std::println("(alias) Length: {}", v.length());
|
||||
std::println("std::get<1>(v): {}", std::get<1>(v));
|
||||
auto [x, y, z] = v;
|
||||
std::println("Bindings: [{}, {}, {}]", x, y, z);
|
||||
|
||||
// Let's mix and match!
|
||||
Vec<6> v3(v, 7, swizzle<"zy">(v2));
|
||||
std::println("{{v, 7, XZ(v2)}}: {}", v3);
|
||||
|
||||
// Scalar operations
|
||||
std::println("v + 3: {}", v + 3);
|
||||
std::println("v - 3: {}", v - 3);
|
||||
std::println("v * 3: {}", v * 3);
|
||||
std::println("v / 3: {}", v / 3);
|
||||
|
||||
std::println("3 + v: {}", 3 + v);
|
||||
std::println("3 - v: {}", 3 - v);
|
||||
std::println("3 * v: {}", 3 * v);
|
||||
std::println("3 / v: {}", 3 / v);
|
||||
|
||||
// Casting
|
||||
auto v4 = static_cast<Vec3d>(v);
|
||||
#ifdef SMATH_IMPLICIT_CONVERSIONS
|
||||
Vec3d v5 = v;
|
||||
#else
|
||||
Vec3d v5 = static_cast<Vec3d>(v);
|
||||
#endif // SMATH_IMPLICIT_CONVERSIONS
|
||||
std::println("Are v4 and v5 same? {}", v4 == v5);
|
||||
}
|
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1755615617,
|
||||
"narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "20075955deac2583bb12f07151c2df830ef346b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
32
flake.nix
Normal file
32
flake.nix
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
system = system;
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
cmake
|
||||
ninja
|
||||
clang-tools
|
||||
lldb
|
||||
pkg-config
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
436
include/smath.hpp
Normal file
436
include/smath.hpp
Normal file
@@ -0,0 +1,436 @@
|
||||
#pragma once
|
||||
|
||||
/* Copyright 2025 Slendi <slendi@socopon.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You can define the following macros to change functionality:
|
||||
* - SMATH_IMPLICIT_CONVERSIONS
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <type_traits>
|
||||
|
||||
namespace smath {
|
||||
|
||||
template <std::size_t N, typename T>
|
||||
requires std::is_arithmetic_v<T>
|
||||
struct VecV;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <std::size_t N> struct FixedString {
|
||||
char data[N]{};
|
||||
static constexpr std::size_t size = N - 1;
|
||||
constexpr FixedString(char const (&s)[N]) {
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
data[i] = s[i];
|
||||
}
|
||||
constexpr char operator[](std::size_t i) const { return data[i]; }
|
||||
};
|
||||
template <class X> struct is_vecv : std::false_type {};
|
||||
template <std::size_t M, class U>
|
||||
struct is_vecv<VecV<M, U>> : std::true_type {};
|
||||
template <class X>
|
||||
inline constexpr bool is_vecv_v = is_vecv<std::remove_cvref_t<X>>::value;
|
||||
template <class X>
|
||||
inline constexpr bool is_scalar_v =
|
||||
std::is_arithmetic_v<std::remove_cvref_t<X>>;
|
||||
template <class X> struct vecv_size;
|
||||
template <std::size_t M, class U>
|
||||
struct vecv_size<VecV<M, U>> : std::integral_constant<std::size_t, M> {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <std::size_t N, typename T = float>
|
||||
requires std::is_arithmetic_v<T>
|
||||
struct VecV : std::array<T, N> {
|
||||
private:
|
||||
template <class X> static consteval std::size_t extent() {
|
||||
if constexpr (detail::is_vecv_v<X>)
|
||||
return detail::vecv_size<std::remove_cvref_t<X>>::value;
|
||||
else if constexpr (detail::is_scalar_v<X>)
|
||||
return 1;
|
||||
else
|
||||
return 0; // Should be unreachable
|
||||
}
|
||||
template <class... Args> static consteval std::size_t total_extent() {
|
||||
return (extent<Args>() + ... + 0);
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
constexpr VecV() noexcept {
|
||||
for (auto &v : *this)
|
||||
v = T(0);
|
||||
}
|
||||
|
||||
explicit constexpr VecV(T const &s) noexcept {
|
||||
for (auto &v : *this)
|
||||
v = s;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
requires((detail::is_scalar_v<Args> || detail::is_vecv_v<Args>) && ...) &&
|
||||
(total_extent<Args...>() == N) &&
|
||||
(!(sizeof...(Args) == 1 && (detail::is_vecv_v<Args> && ...)))
|
||||
constexpr VecV(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.
|
||||
#define VEC_ACC(component, req, idx) \
|
||||
constexpr auto component() noexcept -> T &requires(N >= req) { \
|
||||
return (*this)[idx]; \
|
||||
} constexpr auto component() const->T const & \
|
||||
requires(N >= req) \
|
||||
{ \
|
||||
return (*this)[idx]; \
|
||||
}
|
||||
|
||||
VEC_ACC(r, 1, 0)
|
||||
VEC_ACC(g, 2, 1)
|
||||
VEC_ACC(b, 3, 2)
|
||||
VEC_ACC(a, 4, 3)
|
||||
|
||||
VEC_ACC(x, 1, 0)
|
||||
VEC_ACC(y, 2, 1)
|
||||
VEC_ACC(z, 3, 2)
|
||||
VEC_ACC(w, 4, 3)
|
||||
|
||||
VEC_ACC(s, 1, 0)
|
||||
VEC_ACC(t, 2, 1)
|
||||
VEC_ACC(p, 3, 2)
|
||||
VEC_ACC(q, 4, 3)
|
||||
|
||||
VEC_ACC(u, 1, 0)
|
||||
VEC_ACC(v, 2, 1)
|
||||
#undef VEC_ACC
|
||||
|
||||
// RHS operations
|
||||
friend constexpr auto operator+(T s, VecV const &v) noexcept -> VecV {
|
||||
return v + s;
|
||||
}
|
||||
friend constexpr auto operator-(T s, VecV const &v) noexcept -> VecV {
|
||||
return VecV(s) - v;
|
||||
}
|
||||
friend constexpr auto operator*(T s, VecV const &v) noexcept -> VecV {
|
||||
return v * s;
|
||||
}
|
||||
friend constexpr auto operator/(T s, VecV const &v) noexcept -> VecV {
|
||||
VecV r{};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = s / v[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
// Members
|
||||
#define VEC_OP(op) \
|
||||
constexpr auto operator op(VecV const &rhs) const noexcept -> VecV { \
|
||||
VecV 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 -> VecV { \
|
||||
VecV result{}; \
|
||||
for (std::size_t i = 0; i < N; ++i) { \
|
||||
result[i] = (*this)[i] op rhs; \
|
||||
} \
|
||||
return result; \
|
||||
}
|
||||
VEC_OP(+)
|
||||
VEC_OP(-)
|
||||
VEC_OP(*)
|
||||
VEC_OP(/)
|
||||
#undef VEC_OP
|
||||
#define VEC_OP_ASSIGN(sym) \
|
||||
constexpr VecV &operator sym##=(VecV const &rhs) noexcept { \
|
||||
for (std::size_t i = 0; i < N; ++i) \
|
||||
(*this)[i] sym## = rhs[i]; \
|
||||
return *this; \
|
||||
} \
|
||||
constexpr VecV &operator sym##=(T const &s) noexcept { \
|
||||
for (std::size_t i = 0; i < N; ++i) \
|
||||
(*this)[i] sym## = s; \
|
||||
return *this; \
|
||||
}
|
||||
VEC_OP_ASSIGN(+)
|
||||
VEC_OP_ASSIGN(-)
|
||||
VEC_OP_ASSIGN(*)
|
||||
VEC_OP_ASSIGN(/)
|
||||
#undef VEC_OP_ASSIGN
|
||||
|
||||
constexpr auto magnitude() const noexcept -> T {
|
||||
T total = 0;
|
||||
for (auto const &v : *this)
|
||||
total += v * v;
|
||||
return std::sqrt(total);
|
||||
}
|
||||
constexpr auto length() const noexcept -> T { return this->magnitude(); }
|
||||
|
||||
constexpr VecV normalized_safe(T eps = eps_default) const noexcept {
|
||||
auto m = magnitude();
|
||||
return (m > eps) ? (*this) / m : VecV{};
|
||||
}
|
||||
constexpr VecV normalize_safe(T eps = eps_default) const noexcept {
|
||||
return normalized_safe(eps);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto normalized() noexcept -> VecV<N, T> const {
|
||||
return (*this) / this->magnitude();
|
||||
}
|
||||
[[nodiscard]] constexpr auto normalize() noexcept -> VecV<N, T> const {
|
||||
return this->normalized();
|
||||
}
|
||||
[[nodiscard]] constexpr auto unit() noexcept -> VecV<N, T> const {
|
||||
return this->normalized();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto dot(VecV<N, T> const &other) noexcept -> T {
|
||||
T res = 0;
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
res += (*this)[i] * other[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static constexpr T eps_default = T(1e-6);
|
||||
template <class U = T>
|
||||
[[nodiscard]] constexpr auto
|
||||
approx_equal(VecV 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))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class U = T> constexpr auto magnitude_promoted() const noexcept {
|
||||
using F = std::conditional_t<std::is_floating_point_v<U>, U, double>;
|
||||
F s = 0;
|
||||
for (auto v : *this)
|
||||
s += F(v) * F(v);
|
||||
return std::sqrt(s);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
requires(N == 3)
|
||||
constexpr VecV cross(const VecV &r) const noexcept {
|
||||
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]};
|
||||
}
|
||||
|
||||
constexpr T distance(VecV const &r) const noexcept {
|
||||
return (*this - r).magnitude();
|
||||
}
|
||||
|
||||
constexpr VecV project_onto(VecV const &n) const noexcept {
|
||||
auto d = this->dot(n);
|
||||
auto nn = n.dot(n);
|
||||
return (nn ? (d / nn) * n : VecV());
|
||||
}
|
||||
|
||||
template <class U>
|
||||
requires(std::is_arithmetic_v<U> && N >= 1)
|
||||
constexpr explicit(!std::is_convertible_v<U, T>)
|
||||
VecV(VecV<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 VecV<N, U>() const noexcept {
|
||||
VecV<N, U> r{};
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
r[i] = static_cast<U>((*this)[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <class U>
|
||||
requires(std::is_arithmetic_v<U> && !std::is_same_v<U, T>)
|
||||
constexpr VecV &operator=(VecV<N, U> const &rhs) noexcept {
|
||||
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 {
|
||||
(*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 {
|
||||
(*this)[i++] = static_cast<T>(v);
|
||||
}
|
||||
template <std::size_t M, class U>
|
||||
constexpr void fill_one(std::size_t &i, const VecV<M, U> &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 VecV<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(VecV<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 VecV<N, T> &v) noexcept {
|
||||
static_assert(I < N);
|
||||
return v[I];
|
||||
}
|
||||
template <size_t I, size_t N, class T>
|
||||
constexpr T &&get(VecV<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 VecV<N, T> &&v) noexcept {
|
||||
static_assert(I < N);
|
||||
return std::move(v[I]);
|
||||
}
|
||||
|
||||
template <std::size_t N, typename T = float>
|
||||
requires std::is_arithmetic_v<T>
|
||||
using Vec = std::conditional_t<N == 1, T, VecV<N, T>>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
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')
|
||||
return 1;
|
||||
else if (c == 'b' || c == 'z' || c == 'p')
|
||||
return 2;
|
||||
else if (c == 'a' || c == 'w' || c == 'q')
|
||||
return 3;
|
||||
return static_cast<std::size_t>(-1);
|
||||
}
|
||||
|
||||
constexpr auto is_valid(char c) -> bool {
|
||||
switch (c) {
|
||||
case 'r':
|
||||
case 'g':
|
||||
case 'b':
|
||||
case 'a':
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'z':
|
||||
case 'w':
|
||||
case 's':
|
||||
case 't':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'u':
|
||||
case 'v':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <detail::FixedString S, std::size_t N, typename T, std::size_t... I>
|
||||
constexpr auto swizzle_impl(VecV<N, T> const &v, std::index_sequence<I...>)
|
||||
-> Vec<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");
|
||||
Vec<S.size, T> out{};
|
||||
std::size_t i = 0;
|
||||
((out[i++] = v[char_to_idx(S[I])]), ...);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <FixedString S>
|
||||
concept SwizzleCharsOK = []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
return ((is_valid(S[I])) && ...);
|
||||
}(std::make_index_sequence<S.size>{});
|
||||
|
||||
template <FixedString S, std::size_t N>
|
||||
concept SwizzleInBounds = []<std::size_t... I>(std::index_sequence<I...>) {
|
||||
return ((char_to_idx(S[I]) < N) && ...);
|
||||
}(std::make_index_sequence<S.size>{});
|
||||
|
||||
template <FixedString S, std::size_t 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(VecV<N, T> const &v) -> Vec<S.size, T> {
|
||||
return detail::swizzle_impl<S>(v, std::make_index_sequence<S.size>{});
|
||||
}
|
||||
|
||||
using Vec2 = VecV<2>;
|
||||
using Vec3 = VecV<3>;
|
||||
using Vec4 = VecV<4>;
|
||||
|
||||
using Vec2d = VecV<2, double>;
|
||||
using Vec3d = VecV<3, double>;
|
||||
using Vec4d = VecV<4, double>;
|
||||
|
||||
} // namespace smath
|
||||
|
||||
template <std::size_t N, typename T>
|
||||
requires std::formattable<T, char>
|
||||
struct std::formatter<smath::VecV<N, T>> : std::formatter<T> {
|
||||
constexpr auto parse(std::format_parse_context &ctx) {
|
||||
return std::formatter<T>::parse(ctx);
|
||||
}
|
||||
|
||||
template <typename Ctx>
|
||||
auto format(smath::VecV<N, T> const &v, Ctx &ctx) const {
|
||||
auto out = ctx.out();
|
||||
*out++ = '{';
|
||||
for (std::size_t i = 0; i < N; ++i) {
|
||||
if (i) {
|
||||
*out++ = ',';
|
||||
*out++ = ' ';
|
||||
}
|
||||
out = std::formatter<T>::format(v[i], ctx);
|
||||
}
|
||||
*out++ = '}';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <size_t N, class T>
|
||||
struct tuple_size<smath::VecV<N, T>> : std::integral_constant<size_t, N> {};
|
||||
|
||||
template <size_t I, size_t N, class T>
|
||||
struct tuple_element<I, smath::VecV<N, T>> {
|
||||
static_assert(I < N);
|
||||
using type = T;
|
||||
};
|
||||
} // namespace std
|
Reference in New Issue
Block a user