stellarlib 0.1.0
Loading...
Searching...
No Matches
intrinsics.hpp
1/* clang-format off */
2
3/*
4 stellarlib
5 Copyright (C) 2025-2026 Domán Zana
6
7 This software is provided 'as-is', without any express or implied
8 warranty. In no event will the authors be held liable for any damages
9 arising from the use of this software.
10
11 Permission is granted to anyone to use this software for any purpose,
12 including commercial applications, and to alter it and redistribute it
13 freely, subject to the following restrictions:
14
15 1. The origin of this software must not be misrepresented; you must not
16 claim that you wrote the original software. If you use this software
17 in a product, an acknowledgment in the product documentation would be
18 appreciated but is not required.
19 2. Altered source versions must be plainly marked as such, and must not be
20 misrepresented as being the original software.
21 3. This notice may not be removed or altered from any source distribution.
22*/
23
24#ifndef STELLARLIB_LIN_INTRINSICS_HPP
25#define STELLARLIB_LIN_INTRINSICS_HPP
26
27#include <stellarlib/lin/matrix.hpp>
28
29#include <SDL3/SDL_stdinc.h>
30
31#include <algorithm>
32#include <cmath>
33#include <cstddef>
34#include <cstdint>
35#include <functional>
36#include <memory>
37#include <numbers>
38#include <ranges>
39#include <type_traits>
40
41namespace stellarlib::lin::internal
42{
43#define STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(op, arg1, expr)\
44template <typename T>\
45[[nodiscard]]\
46constexpr auto op(const T arg1) noexcept\
47 requires (std::is_arithmetic_v<T>)\
48{\
49 expr\
50}\
51\
52template <typename T, std::size_t M, std::size_t N>\
53[[nodiscard]]\
54constexpr auto op(const matrix<T, M, N> &(arg1)) noexcept\
55 -> matrix<T, M, N>\
56{\
57 matrix<T, M, N> res;\
58\
59 for (const auto [res, arg1] : std::views::zip(res, arg1)) {\
60 res = op(arg1);\
61 }\
62\
63 return res;\
64}
65
66#define STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(op, arg1, arg2, expr)\
67template <typename T, typename U>\
68[[nodiscard]]\
69constexpr auto op(const T arg1, const U arg2) noexcept\
70 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)\
71{\
72 expr\
73}\
74\
75template <typename T, std::size_t M, std::size_t N, typename U>\
76[[nodiscard]]\
77constexpr auto op(const matrix<T, M, N> &(arg1), const U arg2) noexcept\
78 -> matrix<std::common_type_t<T, U>, M, N>\
79 requires (std::is_arithmetic_v<U>)\
80{\
81 matrix<std::common_type_t<T, U>, M, N> res;\
82\
83 for (const auto [res, arg1] : std::views::zip(res, arg1)) {\
84 res = op(arg1, arg2);\
85 }\
86\
87 return res;\
88}\
89\
90template <typename T, typename U, std::size_t M, std::size_t N>\
91[[nodiscard]]\
92constexpr auto op(const T arg1, const matrix<U, M, N> &(arg2)) noexcept\
93 -> matrix<std::common_type_t<T, U>, M, N>\
94 requires (std::is_arithmetic_v<T>)\
95{\
96 matrix<std::common_type_t<T, U>, M, N> res;\
97\
98 for (const auto [res, arg2] : std::views::zip(res, arg2)) {\
99 res = op(arg1, arg2);\
100 }\
101\
102 return res;\
103}\
104\
105template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>\
106[[nodiscard]]\
107constexpr auto op(const matrix<T, M1, N1> &(arg1), const matrix<U, M2, N2> &(arg2)) noexcept\
108 -> matrix<std::common_type_t<T, U>, M1, N1>\
109 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2 || M1 == M2 && N1 == N2)\
110{\
111 matrix<std::common_type_t<T, U>, M1, N1> res;\
112\
113 for (const auto [res, arg1, arg2] : std::views::zip(res, arg1, arg2)) {\
114 res = op(arg1, arg2);\
115 }\
116\
117 return res;\
118}
119
120#define STELLARLIB_LIN_INTRINSICS_TRIPLE_ARG_OPERATION_IMPL(op, arg1, arg2, arg3, expr)\
121template <typename T, typename U, typename V>\
122[[nodiscard]]\
123constexpr auto op(const T arg1, const U arg2, const V arg3) noexcept\
124 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U> && std::is_arithmetic_v<V>)\
125{\
126 expr\
127}\
128\
129template <typename T, std::size_t M, std::size_t N, typename U, typename V>\
130[[nodiscard]]\
131constexpr auto op(const matrix<T, M, N> &(arg1), const U arg2, const V arg3) noexcept\
132 -> matrix<std::common_type_t<T, U, V>, M, N>\
133 requires (std::is_arithmetic_v<U> && std::is_arithmetic_v<V>)\
134{\
135 matrix<std::common_type_t<T, U, V>, M, N> res;\
136\
137 for (const auto [res, arg1] : std::views::zip(res, arg1)) {\
138 res = op(arg1, arg2, arg3);\
139 }\
140\
141 return res;\
142}\
143\
144template <typename T, typename U, std::size_t M, std::size_t N, typename V>\
145[[nodiscard]]\
146constexpr auto op(const T arg1, const matrix<U, M, N> &(arg2), const V arg3) noexcept\
147 -> matrix<std::common_type_t<T, U, V>, M, N>\
148 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<V>)\
149{\
150 matrix<std::common_type_t<T, U, V>, M, N> res;\
151\
152 for (const auto [res, arg2] : std::views::zip(res, arg2)) {\
153 res = op(arg1, arg2, arg3);\
154 }\
155\
156 return res;\
157}\
158\
159template <typename T, typename U, typename V, std::size_t M, std::size_t N>\
160[[nodiscard]]\
161constexpr auto op(const T arg1, const U arg2, const matrix<V, M, N> &(arg3)) noexcept\
162 -> matrix<std::common_type_t<T, U, V>, M, N>\
163 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)\
164{\
165 matrix<std::common_type_t<T, U, V>, M, N> res;\
166\
167 for (const auto [res, arg3] : std::views::zip(res, arg3)) {\
168 res = op(arg1, arg2, arg3);\
169 }\
170\
171 return res;\
172}\
173\
174template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2, typename V>\
175[[nodiscard]]\
176constexpr auto op(const matrix<T, M1, N1> &(arg1), const matrix<U, M2, N2> &(arg2), const V arg3) noexcept\
177 -> matrix<std::common_type_t<T, U, V>, M1, N1>\
178 requires (std::is_arithmetic_v<V> && ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2 || M1 == M2 && N1 == N2))\
179{\
180 matrix<std::common_type_t<T, U, V>, M1, N1> res;\
181\
182 for (const auto [res, arg1, arg2] : std::views::zip(res, arg1, arg2)) {\
183 res = op(arg1, arg2, arg3);\
184 }\
185\
186 return res;\
187}\
188\
189template <typename T, std::size_t M1, std::size_t N1, typename U, typename V, std::size_t M2, std::size_t N2>\
190[[nodiscard]]\
191constexpr auto op(const matrix<T, M1, N1> &(arg1), const U arg2, const matrix<V, M2, N2> &(arg3)) noexcept\
192 -> matrix<std::common_type_t<T, U, V>, M1, N1>\
193 requires (std::is_arithmetic_v<U> && ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2 || M1 == M2 && N1 == N2))\
194{\
195 matrix<std::common_type_t<T, U, V>, M1, N1> res;\
196\
197 for (const auto [res, arg1, arg3] : std::views::zip(res, arg1, arg3)) {\
198 res = op(arg1, arg2, arg3);\
199 }\
200\
201 return res;\
202}\
203\
204template <typename T, typename U, std::size_t M1, std::size_t N1, typename V, std::size_t M2, std::size_t N2>\
205[[nodiscard]]\
206constexpr auto op(const T arg1, const matrix<U, M1, N1> &(arg2), const matrix<V, M2, N2> &(arg3)) noexcept\
207 -> matrix<std::common_type_t<T, U, V>, M1, N1>\
208 requires (std::is_arithmetic_v<T> && ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2 || M1 == M2 && N1 == N2))\
209{\
210 matrix<std::common_type_t<T, U, V>, M1, N1> res;\
211\
212 for (const auto [res, arg2, arg3] : std::views::zip(res, arg2, arg3)) {\
213 res = op(arg1, arg2, arg3);\
214 }\
215\
216 return res;\
217}\
218\
219template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2, typename V, std::size_t M3, std::size_t N3>\
220[[nodiscard]]\
221constexpr auto op(const matrix<T, M1, N1> &(arg1), const matrix<U, M2, N2> &(arg2), const matrix<V, M3, N3> &(arg3)) noexcept\
222 -> matrix<std::common_type_t<T, U, V>, M1, N1>\
223 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && (M3 == 1 || N3 == 1) && M1 * N1 == M2 * N2 && M2 * N2 == M3 * N3 || M1 == M2 && M2 == M3 && N1 == N2 && N2 == N3)\
224{\
225 matrix<std::common_type_t<T, U, V>, M1, N1> res;\
226\
227 for (const auto [res, arg1, arg2, arg3] : std::views::zip(res, arg1, arg2, arg3)) {\
228 res = op(arg1, arg2, arg3);\
229 }\
230\
231 return res;\
232}
233
234STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(abs, x, {
235 if constexpr (std::is_constant_evaluated()) {
236 return x < 0 ? -x : x;
237 }
238 else {
239 return std::abs(x);
240 }
241});
242
243STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(acos, x, {
244 return std::acos(x);
245});
246
247template <typename T>
248[[nodiscard]]
249constexpr auto all(const T x) noexcept
250 requires (std::is_arithmetic_v<T>)
251{
252 return static_cast<bool>(x);
253}
254
255template <typename T, std::size_t M, std::size_t N>
256[[nodiscard]]
257constexpr auto all(const matrix<T, M, N> &x) noexcept
258{
259 return std::ranges::all_of(x, static_cast<bool (*)(T)>(all));
260}
261
262template <typename T>
263[[nodiscard]]
264constexpr auto any(const T x) noexcept
265 requires (std::is_arithmetic_v<T>)
266{
267 return static_cast<bool>(x);
268}
269
270template <typename T, std::size_t M, std::size_t N>
271[[nodiscard]]
272constexpr auto any(const matrix<T, M, N> &x) noexcept
273{
274 return std::ranges::any_of(x, static_cast<bool (*)(T)>(any));
275}
276
277STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(asin, x, {
278 return std::asin(x);
279});
280
281STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(atan, x, {
282 return std::atan(x);
283});
284
285STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(atan2, y, x, {
286 return std::atan2(y, x);
287});
288
289STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(ceil, x, {
290 return std::ceil(x);
291});
292
293STELLARLIB_LIN_INTRINSICS_TRIPLE_ARG_OPERATION_IMPL(clamp, x, min, max, {
294 return SDL_clamp(x, min, max);
295});
296
297STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(cos, x, {
298 return std::cos(x);
299});
300
301STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(cosh, x, {
302 return std::cosh(x);
303});
304
305template <typename T, typename U>
306[[nodiscard]]
307constexpr auto cross([[maybe_unused]] const T x, [[maybe_unused]] const U y) noexcept
308 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
309{
310 return matrix<std::common_type_t<T, U>, 1, 3>{};
311}
312
313template <typename T, std::size_t M, std::size_t N, typename U>
314[[nodiscard]]
315constexpr auto cross(const matrix<T, M, N> &x, const U y) noexcept
316 requires (std::is_arithmetic_v<U> && M * N == 3)
317{
318 return matrix<std::common_type_t<T, U>, M, N>{x.y() * y - x.z() * y, x.z() * y - x.x() * y, x.x() * y - x.y() * y};
319}
320
321template <typename T, typename U, std::size_t M, std::size_t N>
322[[nodiscard]]
323constexpr auto cross(const T x, const matrix<U, M, N> &y) noexcept
324 requires (std::is_arithmetic_v<T> && M * N == 3)
325{
326 return matrix<std::common_type_t<T, U>, M, N>{x * y.z() - x * y.y(), x * y.x() - x * y.z(), x * y.y() - x * y.x()};
327}
328
329template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>
330[[nodiscard]]
331constexpr auto cross(const matrix<T, M1, N1> &x, const matrix<U, M2, N2> &y) noexcept
332 requires (M1 * N1 == 3 && M2 * N2 == 3)
333{
334 return matrix<std::common_type_t<T, U>, M1, N1>{x.y() * y.z() - x.z() * y.y(), x.z() * y.x() - x.x() * y.z(), x.x() * y.y() - x.y() * y.x()};
335}
336
337STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(degrees, x, {
338 return 180 / std::numbers::pi_v<T> * x;
339});
340
341template <typename T>
342[[nodiscard]]
343constexpr auto determinant(const matrix<T, 1, 1> &m) noexcept
344{
345 return m.front();
346}
347
348template <typename T>
349[[nodiscard]]
350constexpr auto determinant(const matrix<T, 2, 2> &m) noexcept
351{
352 return m[0][0] * m[1][1] - m[0][1] * m[1][0];
353}
354
355template <typename T>
356[[nodiscard]]
357constexpr auto determinant(const matrix<T, 3, 3> &m) noexcept
358{
359 return m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
360}
361
362template <typename T>
363[[nodiscard]]
364constexpr auto determinant(const matrix<T, 4, 4> &m) noexcept
365{
366 return m[0][0] * (m[1][1] * (m[2][2] * m[3][3] - m[2][3] * m[3][2]) - m[1][2] * (m[2][1] * m[3][3] - m[2][3] * m[3][1]) + m[1][3] * (m[2][1] * m[3][2] - m[2][2] * m[3][1])) - m[0][1] * (m[1][0] * (m[2][2] * m[3][3] - m[2][3] * m[3][2]) - m[1][2] * (m[2][0] * m[3][3] - m[2][3] * m[3][0]) + m[1][3] * (m[2][0] * m[3][2] - m[2][2] * m[3][0])) + m[0][2] * (m[1][0] * (m[2][1] * m[3][3] - m[2][3] * m[3][1]) - m[1][1] * (m[2][0] * m[3][3] - m[2][3] * m[3][0]) + m[1][3] * (m[2][0] * m[3][1] - m[2][1] * m[3][0])) - m[0][3] * (m[1][0] * (m[2][1] * m[3][2] - m[2][2] * m[3][1]) - m[1][1] * (m[2][0] * m[3][2] - m[2][2] * m[3][0]) + m[1][2] * (m[2][0] * m[3][1] - m[2][1] * m[3][0]));
367}
368
369template <typename T, std::size_t N>
370[[nodiscard]]
371constexpr auto determinant(matrix<T, N, N> m) noexcept
372{
373 T determinant{1};
374
375 for (const auto i : std::views::iota(std::size_t{}, N)) {
376 auto pivot{i};
377
378 for (const auto j : std::views::iota(i + 1, N)) {
379 if (abs(m[pivot][i]) < abs(m[j][i])) {
380 pivot = j;
381 }
382 }
383
384 if (pivot != i) {
385 for (const auto j : std::views::iota(i, N)) {
386 std::swap(m[i][j], m[pivot][j]);
387 }
388
389 determinant *= -1;
390 }
391
392 if (!static_cast<bool>(m[i][i])) {
393 return T{};
394 }
395
396 determinant *= m[i][i];
397
398 for (const auto j : std::views::iota(i + 1, N)) {
399 m[j][i] /= m[i][i];
400
401 for (const auto k : std::views::iota(i + 1, N)) {
402 m[j][k] -= m[j][i] * m[i][k];
403 }
404 }
405 }
406
407 return determinant;
408}
409
410STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(sqrt, x, {
411 return std::sqrt(x);
412});
413
414template <typename T>
415[[nodiscard]]
416constexpr auto length(const T x) noexcept
417 requires (std::is_arithmetic_v<T>)
418{
419 return abs(x);
420}
421
422template <typename T, std::size_t M, std::size_t N>
423[[nodiscard]]
424constexpr auto length(const matrix<T, M, N> &x) noexcept
425 requires (M == 1 || N == 1)
426{
427 if constexpr (M * N == 2) {
428 return std::hypot(x.x(), x.y());
429 }
430 else if constexpr (M * N == 3) {
431 return std::hypot(x.x(), x.y(), x.z());
432 }
433 else {
434 return std::ranges::fold_left(x, 0, static_cast<T (*)(T, T)>(std::hypot));
435 }
436}
437
438template <typename T, typename U>
439[[nodiscard]]
440constexpr auto distance(const T x, const U y) noexcept
441 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
442{
443 return length(x - y);
444}
445
446template <typename T, std::size_t M, std::size_t N, typename U>
447[[nodiscard]]
448constexpr auto distance(const matrix<T, M, N> &x, const U y) noexcept
449 requires (std::is_arithmetic_v<U> && (M == 1 || N == 1))
450{
451 return length(x - y);
452}
453
454template <typename T, typename U, std::size_t M, std::size_t N>
455[[nodiscard]]
456constexpr auto distance(const T x, const matrix<U, M, N> &y) noexcept
457 requires (std::is_arithmetic_v<T> && (M == 1 || N == 1))
458{
459 return length(x - y);
460}
461
462template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>
463[[nodiscard]]
464constexpr auto distance(const matrix<T, M1, N1> &x, const matrix<U, M2, N2> &y) noexcept
465 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2)
466{
467 return length(x - y);
468}
469
470template <typename T, typename U>
471[[nodiscard]]
472constexpr auto dot(const T x, const U y) noexcept
473 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
474{
475 return x * y;
476}
477
478template <typename T, std::size_t M, std::size_t N, typename U>
479[[nodiscard]]
480constexpr auto dot(const matrix<T, M, N> &x, const U y) noexcept
481 requires (std::is_arithmetic_v<U> && (M == 1 || N == 1))
482{
483 return std::ranges::fold_left(x | std::views::transform([y] [[nodiscard]] (const auto x) noexcept -> auto {
484 return x * y;
485 }), 0, std::plus{});
486}
487
488template <typename T, typename U, std::size_t M, std::size_t N>
489[[nodiscard]]
490constexpr auto dot(const T x, const matrix<U, M, N> &y) noexcept
491 requires (std::is_arithmetic_v<T> && (M == 1 || N == 1))
492{
493 return std::ranges::fold_left(y | std::views::transform([x] [[nodiscard]] (const auto y) noexcept -> auto {
494 return x * y;
495 }), 0, std::plus{});
496}
497
498template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>
499[[nodiscard]]
500constexpr auto dot(const matrix<T, M1, N1> &x, const matrix<U, M2, N2> &y) noexcept
501 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2)
502{
503 return std::ranges::fold_left(std::views::zip(x, y) | std::views::transform([] [[nodiscard]] (const auto xy) noexcept -> auto {
504 return std::get<0>(xy) * std::get<1>(xy);
505 }), 0, std::plus{});
506}
507
508template <typename T, typename U>
509[[nodiscard]]
510constexpr auto dst(const T src0, const U src1) noexcept
511 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
512{
513 return matrix<std::common_type_t<T, U>, 1, 4>{1, src0 * src1, src0, src1};
514}
515
516template <typename T, std::size_t M, std::size_t N, typename U>
517[[nodiscard]]
518constexpr auto dst(const matrix<T, M, N> &src0, const U src1) noexcept
519 requires (std::is_arithmetic_v<U> && (M == 1 || N == 1) && M * N == 4)
520{
521 return matrix<std::common_type_t<T, U>, M, N>{1, src0.y() * src1, src0.z(), src1};
522}
523
524template <typename T, typename U, std::size_t M, std::size_t N>
525[[nodiscard]]
526constexpr auto dst(const T src0, const matrix<U, M, N> &src1) noexcept
527 requires (std::is_arithmetic_v<T> && (M == 1 || N == 1) && M * N == 4)
528{
529 return matrix<std::common_type_t<T, U>, M, N>{1, src0 * src1.y(), src0, src1.w()};
530}
531
532template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>
533[[nodiscard]]
534constexpr auto dst(const matrix<T, M1, N1> &src0, const matrix<U, M2, N2> &src1) noexcept
535 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == 4 && M2 * N2 == 4)
536{
537 return matrix<std::common_type_t<T, U>, M1, N1>{1, src0.y() * src1.y(), src0.z(), src1.w()};
538}
539
540STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(exp, x, {
541 return std::exp(x);
542});
543
544STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(exp2, x, {
545 return std::exp2(x);
546});
547
548template <typename T, typename U, typename V>
549[[nodiscard]]
550constexpr auto faceforward(const T n, const U i, const V ng) noexcept
551 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U> && std::is_arithmetic_v<V>)
552{
553 return std::common_type_t<T, U, V>{dot(i, ng) < 0 ? n : -n};
554}
555
556template <typename T, std::size_t M, std::size_t N, typename U, typename V>
557[[nodiscard]]
558constexpr auto faceforward(const matrix<T, M, N> &n, const U i, const V ng) noexcept
559 requires (std::is_arithmetic_v<U> && std::is_arithmetic_v<V> && (M == 1 || N == 1))
560{
561 return matrix<std::common_type_t<T, U, V>, M, N>{dot(i, ng) < 0 ? n : -n};
562}
563
564template <typename T, typename U, std::size_t M, std::size_t N, typename V>
565[[nodiscard]]
566constexpr auto faceforward(const T n, const matrix<U, M, N> &i, const V ng) noexcept
567 -> matrix<std::common_type_t<T, U, V>, M, N>
568 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<V> && (M == 1 || N == 1))
569{
570 matrix<std::common_type_t<T, U, V>, M, N> res;
571
572 if (dot(i, ng) < 0) {
573 for (auto &res : res) {
574 res = n;
575 }
576 }
577 else {
578 for (auto &res : res) {
579 res = -n;
580 }
581 }
582
583 return res;
584}
585
586template <typename T, typename U, typename V, std::size_t M, std::size_t N>
587[[nodiscard]]
588constexpr auto faceforward(const T n, const U i, const matrix<V, M, N> &ng) noexcept
589 -> matrix<std::common_type_t<T, U, V>, M, N>
590 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U> && (M == 1 || N == 1))
591{
592 matrix<std::common_type_t<T, U, V>, M, N> res;
593
594 if (dot(i, ng) < 0) {
595 for (auto &res : res) {
596 res = n;
597 }
598 }
599 else {
600 for (auto &res : res) {
601 res = -n;
602 }
603 }
604
605 return res;
606}
607
608template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2, typename V>
609[[nodiscard]]
610constexpr auto faceforward(const matrix<T, M1, N1> &n, const matrix<U, M2, N2> &i, const V ng) noexcept
611 requires (std::is_arithmetic_v<V> && (M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2)
612{
613 return matrix<std::common_type_t<T, U, V>, M1, N1>{dot(i, ng) < 0 ? n : -n};
614}
615
616template <typename T, std::size_t M1, std::size_t N1, typename U, typename V, std::size_t M2, std::size_t N2>
617[[nodiscard]]
618constexpr auto faceforward(const matrix<T, M1, N1> &n, const U i, const matrix<V, M2, N2> &ng) noexcept
619 requires (std::is_arithmetic_v<U> && (M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2)
620{
621 return matrix<std::common_type_t<T, U, V>, M1, N1>{dot(i, ng) < 0 ? n : -n};
622}
623
624template <typename T, typename U, std::size_t M1, std::size_t N1, typename V, std::size_t M2, std::size_t N2>
625[[nodiscard]]
626constexpr auto faceforward(const T n, const matrix<U, M1, N1> &i, const matrix<V, M2, N2> &ng) noexcept
627 -> matrix<std::common_type_t<T, U, V>, M1, N1>
628 requires (std::is_arithmetic_v<T> && (M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2)
629{
630 matrix<std::common_type_t<T, U, V>, M1, N1> res;
631
632 if (dot(i, ng) < 0) {
633 for (auto &res : res) {
634 res = n;
635 }
636 }
637 else {
638 for (auto &res : res) {
639 res = -n;
640 }
641 }
642
643 return res;
644}
645
646template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2, typename V, std::size_t M3, std::size_t N3>
647[[nodiscard]]
648constexpr auto faceforward(const matrix<T, M1, N1> &n, const matrix<U, M2, N2> &i, const matrix<V, M3, N3> &ng) noexcept
649 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && (M3 == 1 || N3 == 1) && M1 * N1 == M2 * N2 && M2 * N2 == M3 * N3)
650{
651 return matrix<std::common_type_t<T, U, V>, M1, N1>{dot(i, ng) < 0 ? n : -n};
652}
653
654STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(floor, x, {
655 return std::floor(x);
656});
657
658STELLARLIB_LIN_INTRINSICS_TRIPLE_ARG_OPERATION_IMPL(fma, a, b, c, {
659 return std::fma(a, b, c);
660});
661
662STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(fmod, x, y, {
663 return std::fmod(x, y);
664});
665
666STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(frac, x, {
667 return x - floor(x);
668});
669
670STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(ldexp, x, exp, {
671 return std::ldexp(x, static_cast<std::int32_t>(exp));
672});
673
674STELLARLIB_LIN_INTRINSICS_TRIPLE_ARG_OPERATION_IMPL(lerp, x, y, s, {
675 return std::lerp(x, y, s);
676});
677
678STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(max, x, y, {
679 return SDL_max(x, y);
680});
681
682STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(pow, x, y, {
683 return std::pow(x, y);
684});
685
686template <typename T, typename U, typename V>
687[[nodiscard]]
688constexpr auto lit(const T n_dot_l, const U n_dot_h, const V m) noexcept
689 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U> && std::is_arithmetic_v<V>)
690{
691 return matrix<std::common_type_t<T, U, V>, 1, 4>{1, max(n_dot_l, std::common_type_t<T, U, V>{}), n_dot_l < 0 || n_dot_h < 0 ? 0 : pow(n_dot_h, m), 1};
692}
693
694STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(log, x, {
695 return std::log(x);
696});
697
698STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(log10, x, {
699 return std::log10(x);
700});
701
702STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(log2, x, {
703 return std::log2(x);
704});
705
706STELLARLIB_LIN_INTRINSICS_TRIPLE_ARG_OPERATION_IMPL(mad, mvalue, avalue, bvalue, {
707 if constexpr (std::is_constant_evaluated()) {
708 return mvalue * avalue + bvalue;
709 }
710 else {
711 return fma(mvalue, avalue, bvalue);
712 }
713});
714
715STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(min, x, y, {
716 return SDL_min(x, y);
717});
718
719template <typename T, typename U>
720[[nodiscard]]
721constexpr auto modf(const T x, U &ip) noexcept
722 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
723{
724 return std::modf(x, std::addressof(ip));
725}
726
727template <typename T, typename U, std::size_t M, std::size_t N>
728[[nodiscard]]
729constexpr auto modf(const T x, matrix<U, M, N> &ip) noexcept
731 requires (std::is_arithmetic_v<T>)
732{
734 res.front() = modf(x, ip.front());
735
736 for (const auto i : std::views::iota(std::size_t{1}, M * N)) {
737 res.std::template array<std::common_type_t<T, U>, M * N>::operator[](i) = res.front();
738 ip.std::template array<U, M * N>::operator[](i) = ip.front();
739 }
740
741 return res;
742}
743
744template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>
745[[nodiscard]]
746constexpr auto modf(const matrix<T, M1, N1> &x, matrix<U, M2, N2> &ip) noexcept
747 -> matrix<std::common_type_t<T, U>, M1, N1>
748 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2 || M1 == M2 && N1 == N2)
749{
750 matrix<std::common_type_t<T, U>, M1, N1> res;
751
752 for (const auto [res, x, ip] : std::views::zip(res, x, ip)) {
753 res = modf(x, ip);
754 }
755
756 return res;
757}
758
759template <typename T, typename U>
760[[nodiscard]]
761constexpr auto mul(const T x, const U y) noexcept
762 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
763{
764 return x * y;
765}
766
767template <typename T, std::size_t M, std::size_t N, typename U>
768[[nodiscard]]
769constexpr auto mul(const matrix<T, M, N> &x, const U y) noexcept
770 requires (std::is_arithmetic_v<U>)
771{
772 return x * y;
773}
774
775template <typename T, typename U, std::size_t M, std::size_t N>
776[[nodiscard]]
777constexpr auto mul(const T x, const matrix<U, M, N> &y) noexcept
778 requires (std::is_arithmetic_v<T>)
779{
780 return x * y;
781}
782
783template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>
784[[nodiscard]]
785constexpr auto mul(const matrix<T, M1, N1> &x, const matrix<U, M2, N2> &y) noexcept
786 -> matrix<std::common_type_t<T, U>, M1 == 1 || N1 == 1 ? 1 : M1, M2 == 1 || N2 == 1 ? 1 : N2>
787 requires ((M1 == 1 || N1 == 1) && M1 * N1 == M2 || (M2 == 1 || N2 == 1) && M2 * N2 == N1 || N1 == M2)
788{
789 constexpr auto M{M1 == 1 || N1 == 1 ? 1 : M1};
790 constexpr auto N{(M1 == 1 || N1 == 1) && M1 * N1 == M2 ? M2 : N1};
791 constexpr auto P{M2 == 1 || N2 == 1 ? 1 : N2};
792 matrix<std::common_type_t<T, U>, M, P> res{};
793
794 for (const auto m : std::views::iota(std::size_t{}, M)) {
795 for (const auto n : std::views::iota(std::size_t{}, N)) {
796 for (const auto p : std::views::iota(std::size_t{}, P)) {
797 res.std::template array<T, M * P>::operator[](mad(m, P, p)) += x.std::template array<T, M * N>::operator[](mad(m, N, n)) * y.std::template array<T, N * P>::operator[](mad(n, P, p));
798 }
799 }
800 }
801
802 return res;
803}
804
805template <typename T>
806[[nodiscard]]
807constexpr auto normalize(const T x) noexcept
808 requires (std::is_arithmetic_v<T>)
809{
810 return x / length(x);
811}
812
813template <typename T, std::size_t M, std::size_t N>
814[[nodiscard]]
815constexpr auto normalize(const matrix<T, M, N> &x) noexcept
816 requires (M == 1 || N == 1)
817{
818 return x / length(x);
819}
820
821STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(radians, x, {
822 return std::numbers::pi_v<T> / 180 * x;
823});
824
825STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(rcp, x, {
826 return 1 / x;
827});
828
829template <typename T, typename U>
830[[nodiscard]]
831constexpr auto reflect(const T i, const U n) noexcept
832 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
833{
834 return i - std::common_type_t<T, U>{2} * n * dot(i, n);
835}
836
837template <typename T, std::size_t M, std::size_t N, typename U>
838[[nodiscard]]
839constexpr auto reflect(const matrix<T, M, N> &i, const U n) noexcept
840 requires (std::is_arithmetic_v<U> && (M == 1 || N == 1))
841{
842 return i - std::common_type_t<T, U>{2} * n * dot(i, n);
843}
844
845template <typename T, typename U, std::size_t M, std::size_t N>
846[[nodiscard]]
847constexpr auto reflect(const T i, const matrix<U, M, N> &n) noexcept
848 requires (std::is_arithmetic_v<T> && (M == 1 || N == 1))
849{
850 return i - std::common_type_t<T, U>{2} * n * dot(i, n);
851}
852
853template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2>
854[[nodiscard]]
855constexpr auto reflect(const matrix<T, M1, N1> &i, const matrix<U, M2, N2> &n) noexcept
856 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2)
857{
858 return i - std::common_type_t<T, U>{2} * n * dot(i, n);
859}
860
861#define STELLARLIB_LIN_INTRINSICS_REFRACT_IMPL(zero)\
862{\
863 const auto n_dot_i{dot(n, i)};\
864 const auto k{1 - eta * eta * (1 - n_dot_i * n_dot_i)};\
865\
866 if (k < 0) {\
867 return zero;\
868 }\
869\
870 return eta * i - mad(eta, n_dot_i, sqrt(k)) * n;\
871}
872
873template <typename T, typename U, typename V>
874[[nodiscard]]
875constexpr auto refract(const T i, const U n, const V eta) noexcept
876 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U> && std::is_arithmetic_v<V>)
877STELLARLIB_LIN_INTRINSICS_REFRACT_IMPL((std::common_type_t<T, U, V>{}));
878
879template <typename T, std::size_t M, std::size_t N, typename U, typename V>
880[[nodiscard]]
881constexpr auto refract(const matrix<T, M, N> &i, const U n, const V eta) noexcept
882 requires (std::is_arithmetic_v<U> && std::is_arithmetic_v<V> && (M == 1 || N == 1))
883STELLARLIB_LIN_INTRINSICS_REFRACT_IMPL((matrix<std::common_type_t<T, U, V>, M, N>{}));
884
885template <typename T, typename U, std::size_t M, std::size_t N, typename V>
886[[nodiscard]]
887constexpr auto refract(const T i, const matrix<U, M, N> &n, const V eta) noexcept
888 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<V> && (M == 1 || N == 1))
889STELLARLIB_LIN_INTRINSICS_REFRACT_IMPL((matrix<std::common_type_t<T, U, V>, M, N>{}));
890
891template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2, typename V>
892[[nodiscard]]
893constexpr auto refract(const matrix<T, M1, N1> &i, const matrix<U, M2, N2> &n, const V eta) noexcept
894 requires (std::is_arithmetic_v<V> && (M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2)
895STELLARLIB_LIN_INTRINSICS_REFRACT_IMPL((matrix<std::common_type_t<T, U, V>, M1, N1>{}));
896
897STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(round, x, {
898 return std::round(x);
899});
900
901STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(rsqrt, x, {
902 return rcp(sqrt(x));
903});
904
905STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(saturate, x, {
906 return clamp(x, T{0}, T{1});
907});
908
909STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(sign, x, {
910 return static_cast<T>((0 < x) - (x < 0));
911});
912
913STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(sin, x, {
914 return std::sin(x);
915});
916
917template <typename T, typename U, typename V>
918constexpr void sincos(const T x, U &s, V &c) noexcept
919 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U> && std::is_arithmetic_v<V>)
920{
921 s = sin(x);
922 c = cos(x);
923}
924
925template <typename T, typename U, std::size_t M, std::size_t N, typename V>
926constexpr void sincos(const T x, matrix<U, M, N> &s, V &c) noexcept
927 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<V>)
928{
929 sincos(x, s.front(), c);
930
931 for (const auto i : std::views::iota(std::size_t{1}, M * N)) {
932 s.std::template array<U, M * N>::operator[](i) = s.front();
933 }
934}
935
936template <typename T, typename U, typename V, std::size_t M, std::size_t N>
937constexpr void sincos(const T x, U &s, matrix<V, M, N> &c) noexcept
938 requires (std::is_arithmetic_v<T> && std::is_arithmetic_v<U>)
939{
940 sincos(x, s, c.front());
941
942 for (const auto i : std::views::iota(std::size_t{1}, M * N)) {
943 c.std::template array<V, M * N>::operator[](i) = c.front();
944 }
945}
946
947template <typename T, typename U, std::size_t M1, std::size_t N1, typename V, std::size_t M2, std::size_t N2>
948constexpr void sincos(const T x, matrix<U, M1, N1> &s, matrix<V, M2, N2> &c) noexcept
949 requires (std::is_arithmetic_v<T> && ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && M1 * N1 == M2 * N2 || M1 == M2 && N2 == N1))
950{
951 sincos(x, s.front(), c.front());
952
953 for (const auto i : std::views::iota(std::size_t{1}, M1 * N1)) {
954 s.std::template array<U, M1 * N1>::operator[](i) = s.front();
955 c.std::template array<V, M2 * N2>::operator[](i) = c.front();
956 }
957}
958
959template <typename T, std::size_t M1, std::size_t N1, typename U, std::size_t M2, std::size_t N2, typename V, std::size_t M3, std::size_t N3>
960constexpr void sincos(const matrix<T, M1, N1> &x, matrix<U, M2, N2> &s, matrix<V, M3, N3> &c) noexcept
961 requires ((M1 == 1 || N1 == 1) && (M2 == 1 || N2 == 1) && (M3 == 1 || N3 == 1) && M1 * N1 == M2 * N2 && M2 * N2 == M3 * N3 || M1 == M2 && M2 == M3 && N1 == N2 && N2 == N3)
962{
963 for (const auto [x, s, c] : std::views::zip(x, s, c)) {
964 sincos(x, s, c);
965 }
966}
967
968STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(sinh, x, {
969 return std::sinh(x);
970});
971
972STELLARLIB_LIN_INTRINSICS_TRIPLE_ARG_OPERATION_IMPL(smoothstep, min, max, x, {
973 const auto t{saturate((x - min) / (max - min))};
974 return t * t * (3 - 2 * t);
975});
976
977STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL(step, y, x, {
978 return x < y ? (std::common_type_t<T, U>{0}) : (std::common_type_t<T, U>{1});
980
981STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(tan, x, {
982 return std::tan(x);
983});
984
985STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(tanh, x, {
986 return std::tanh(x);
987});
988
989template <typename T>
990[[nodiscard]]
991constexpr auto transpose(const T x) noexcept
992 requires (std::is_arithmetic_v<T>)
993{
994 return x;
995}
996
997template <typename T, std::size_t M, std::size_t N>
998[[nodiscard]]
999constexpr auto transpose(const matrix<T, M, N> &x) noexcept
1001{
1002 matrix<T, N, M> res;
1003
1004 for (const auto m : std::views::iota(std::size_t{}, M)) {
1005 for (const auto n : std::views::iota(std::size_t{}, N)) {
1006 res.std::template array<T, N * M>::operator[](mad(n, M, m)) = x.std::template array<T, M * N>::operator[](mad(m, N, n));
1007 }
1008 }
1009
1010 return res;
1011}
1012
1013STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL(trunc, x, {
1014 return std::trunc(x);
1016}
1017
1018#undef STELLARLIB_LIN_INTRINSICS_REFRACT_IMPL
1019#undef STELLARLIB_LIN_INTRINSICS_TRIPLE_ARG_OPERATION_IMPL
1020#undef STELLARLIB_LIN_INTRINSICS_DOUBLE_ARG_OPERATION_IMPL
1021#undef STELLARLIB_LIN_INTRINSICS_SINGLE_ARG_OPERATION_IMPL
1022
1023#endif
constexpr auto all(const T x) noexcept
Determines if all components of x are truthy.
Definition intrinsics.hpp:249
constexpr auto dst(const T src0, const U src1) noexcept
Calculates a distance vector for lighting.
Definition intrinsics.hpp:510
constexpr auto cos(const T x) noexcept
Returns the cosine of x (per-component).
Definition intrinsics.hpp:299
constexpr void sincos(const T x, U &s, V &c) noexcept
Returns the sine and cosine of x (per-component).
Definition intrinsics.hpp:918
constexpr auto determinant(const matrix< T, 1, 1 > &m) noexcept
Returns the determinant of the specified square matrix.
Definition intrinsics.hpp:343
constexpr auto pow(const T x, const U y) noexcept
Returns x raised to y (per-component).
Definition intrinsics.hpp:684
constexpr auto dot(const T x, const U y) noexcept
Returns the dot product of two vectors.
Definition intrinsics.hpp:472
constexpr auto sin(const T x) noexcept
Returns the sine of x (per-component).
Definition intrinsics.hpp:915
constexpr auto faceforward(const T n, const U i, const V ng) noexcept
Flips the surface-normal (if needed) to face in a direction opposite to i; returns the result in n.
Definition intrinsics.hpp:550
constexpr auto abs(const T x) noexcept
Returns the absolute value of x (per-component).
Definition intrinsics.hpp:241
constexpr auto cross(const T x, const U y) noexcept
Returns the cross product of two 3D vectors.
Definition intrinsics.hpp:307
constexpr auto transpose(const T x) noexcept
Transposes the specified input matrix.
Definition intrinsics.hpp:991
constexpr auto mul(const T x, const U y) noexcept
Multiplies x and y using matrix math; the inner dimension x-columns and y-rows must be equal.
Definition intrinsics.hpp:761
constexpr auto distance(const T x, const U y) noexcept
Returns a distance scalar between two vectors.
Definition intrinsics.hpp:440
constexpr auto reflect(const T i, const U n) noexcept
Returns a reflection vector using an incident ray and a surface normal.
Definition intrinsics.hpp:831
constexpr auto any(const T x) noexcept
Determines if any components of x are truthy.
Definition intrinsics.hpp:264
constexpr auto modf(const T x, U &ip) noexcept
Splits the value x into fractional and integer parts, each of which has the same sign as x (per-compo...
Definition intrinsics.hpp:721
internal::matrix< T, M, N > matrix
Generic M*N matrix with per-component operations.
Definition lin.hpp:48
constexpr auto max(const T x, const U y) noexcept
Selects the greater of x and y (per-component).
Definition intrinsics.hpp:680
constexpr auto normalize(const T x) noexcept
Normalizes the specified vector according to x/length(x).
Definition intrinsics.hpp:807
constexpr auto length(const T x) noexcept
Returns the length of the specified vector.
Definition intrinsics.hpp:416
constexpr auto refract(const T i, const U n, const V eta) noexcept
Returns a refraction vector using an entering ray, a surface normal, and a refraction index.
Definition intrinsics.hpp:875
constexpr auto lit(const T n_dot_l, const U n_dot_h, const V m) noexcept
Returns a lighting coefficient vector.
Definition intrinsics.hpp:688