diff --git a/build/assets/include/linalg.hpp b/build/assets/include/linalg.hpp new file mode 100644 index 0000000..b5d9848 --- /dev/null +++ b/build/assets/include/linalg.hpp @@ -0,0 +1,669 @@ +// linalg.h - 2.2 - Single-header public domain linear algebra library +// +// The intent of this library is to provide the bulk of the functionality +// you need to write programs that frequently use small, fixed-size vectors +// and matrices, in domains such as computational geometry or computer +// graphics. It strives for terse, readable source code. +// +// The original author of this software is Sterling Orsten, and its permanent +// home is . If you find this software +// useful, an acknowledgement in your source text and/or product documentation +// is appreciated, but not required. +// +// The author acknowledges significant insights and contributions by: +// Stan Melax +// Dimitri Diakopoulos +// +// Some features are deprecated. Define LINALG_FORWARD_COMPATIBLE to remove them. + + + +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + + + +#pragma once +#ifndef LINALG_H +#define LINALG_H + +#include +#include +#include + +namespace linalg +{ + // Small, fixed-length vector type, consisting of exactly M elements of type T, and presumed to be a column-vector unless otherwise noted. + template struct vec; + + // Small, fixed-size matrix type, consisting of exactly M rows and N columns of type T, stored in column-major order. + template struct mat; + + // Specialize converter with a function application operator that converts type U to type T to enable implicit conversions + template struct converter {}; + namespace detail + { + template using conv_t = typename std::enable_if::value, decltype(converter{}(std::declval()))>::type; + + // Trait for retrieving scalar type of any linear algebra object + template struct scalar_type {}; + template struct scalar_type> { using type = T; }; + template struct scalar_type> { using type = T; }; + + // Type returned by the compare(...) function which supports all six comparison operators against 0 + template struct ord { T a,b; }; + template constexpr bool operator == (const ord & o, std::nullptr_t) { return o.a == o.b; } + template constexpr bool operator != (const ord & o, std::nullptr_t) { return !(o.a == o.b); } + template constexpr bool operator < (const ord & o, std::nullptr_t) { return o.a < o.b; } + template constexpr bool operator > (const ord & o, std::nullptr_t) { return o.b < o.a; } + template constexpr bool operator <= (const ord & o, std::nullptr_t) { return !(o.b < o.a); } + template constexpr bool operator >= (const ord & o, std::nullptr_t) { return !(o.a < o.b); } + + // Patterns which can be used with the compare(...) function + template struct any_compare {}; + template struct any_compare,vec> { using type=ord; constexpr ord operator() (const vec & a, const vec & b) const { return ord{a.x,b.x}; } }; + template struct any_compare,vec> { using type=ord; constexpr ord operator() (const vec & a, const vec & b) const { return !(a.x==b.x) ? ord{a.x,b.x} : ord{a.y,b.y}; } }; + template struct any_compare,vec> { using type=ord; constexpr ord operator() (const vec & a, const vec & b) const { return !(a.x==b.x) ? ord{a.x,b.x} : !(a.y==b.y) ? ord{a.y,b.y} : ord{a.z,b.z}; } }; + template struct any_compare,vec> { using type=ord; constexpr ord operator() (const vec & a, const vec & b) const { return !(a.x==b.x) ? ord{a.x,b.x} : !(a.y==b.y) ? ord{a.y,b.y} : !(a.z==b.z) ? ord{a.z,b.z} : ord{a.w,b.w}; } }; + template struct any_compare,mat> { using type=ord; constexpr ord operator() (const mat & a, const mat & b) const { return compare(a.x,b.x); } }; + template struct any_compare,mat> { using type=ord; constexpr ord operator() (const mat & a, const mat & b) const { return a.x!=b.x ? compare(a.x,b.x) : compare(a.y,b.y); } }; + template struct any_compare,mat> { using type=ord; constexpr ord operator() (const mat & a, const mat & b) const { return a.x!=b.x ? compare(a.x,b.x) : a.y!=b.y ? compare(a.y,b.y) : compare(a.z,b.z); } }; + template struct any_compare,mat> { using type=ord; constexpr ord operator() (const mat & a, const mat & b) const { return a.x!=b.x ? compare(a.x,b.x) : a.y!=b.y ? compare(a.y,b.y) : a.z!=b.z ? compare(a.z,b.z) : compare(a.w,b.w); } }; + + // Helper for compile-time index-based access to members of vector and matrix types + template struct getter; + template<> struct getter<0> { template constexpr auto operator() (A & a) const -> decltype(a.x) { return a.x; } }; + template<> struct getter<1> { template constexpr auto operator() (A & a) const -> decltype(a.y) { return a.y; } }; + template<> struct getter<2> { template constexpr auto operator() (A & a) const -> decltype(a.z) { return a.z; } }; + template<> struct getter<3> { template constexpr auto operator() (A & a) const -> decltype(a.w) { return a.w; } }; + + // Stand-in for std::integer_sequence/std::make_integer_sequence + template struct seq {}; + template struct make_seq_impl; + template struct make_seq_impl { using type=seq<>; }; + template struct make_seq_impl { using type=seq; }; + template struct make_seq_impl { using type=seq; }; + template struct make_seq_impl { using type=seq; }; + template struct make_seq_impl { using type=seq; }; + template using make_seq = typename make_seq_impl::type; + template vec constexpr swizzle(const vec & v, seq i [[maybe_unused]]) { return {getter{}(v)...}; } + template mat constexpr swizzle(const mat & m, seq i, seq j [[maybe_unused]]) { return {swizzle(getter{}(m),i)...}; } + + // SFINAE helpers to determine result of function application + template using ret_t = decltype(std::declval()(std::declval()...)); + + // SFINAE helper which is defined if all provided types are scalars + struct empty {}; + template struct scalars; + template<> struct scalars<> { using type=void; }; + template struct scalars : std::conditional::value, scalars, empty>::type {}; + template using scalars_t = typename scalars::type; + + // Helpers which indicate how apply(F, ...) should be called for various arguments + template struct apply {}; // Patterns which contain only vectors or scalars + template struct apply, vec > { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, const vec & a ) { return {f(getter{}(a) )...}; } }; + template struct apply, vec, vec > { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, const vec & a, const vec & b ) { return {f(getter{}(a), getter{}(b) )...}; } }; + template struct apply, vec, B > { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, const vec & a, B b ) { return {f(getter{}(a), b )...}; } }; + template struct apply, A, vec > { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, A a, const vec & b ) { return {f(a, getter{}(b) )...}; } }; + template struct apply, vec, vec, vec> { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, const vec & a, const vec & b, const vec & c) { return {f(getter{}(a), getter{}(b), getter{}(c))...}; } }; + template struct apply, vec, vec, C > { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, const vec & a, const vec & b, C c) { return {f(getter{}(a), getter{}(b), c )...}; } }; + template struct apply, vec, B, vec> { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, const vec & a, B b, const vec & c) { return {f(getter{}(a), b, getter{}(c))...}; } }; + template struct apply, vec, B, C > { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, const vec & a, B b, C c) { return {f(getter{}(a), b, c )...}; } }; + template struct apply, A, vec, vec> { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, A a, const vec & b, const vec & c) { return {f(a, getter{}(b), getter{}(c))...}; } }; + template struct apply, A, vec, C > { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, A a, const vec & b, C c) { return {f(a, getter{}(b), c )...}; } }; + template struct apply, A, B, vec> { using type=vec,M>; enum {size=M, mm=0}; template static constexpr type impl(seq, F f, A a, B b, const vec & c) { return {f(a, b, getter{}(c))...}; } }; + template struct apply, mat > { using type=mat,M,N>; enum {size=N, mm=0}; template static constexpr type impl(seq, F f, const mat & a ) { return {apply >::impl(make_seq<0,M>{}, f, getter{}(a) )...}; } }; + template struct apply, mat, mat> { using type=mat,M,N>; enum {size=N, mm=1}; template static constexpr type impl(seq, F f, const mat & a, const mat & b) { return {apply, vec>::impl(make_seq<0,M>{}, f, getter{}(a), getter{}(b))...}; } }; + template struct apply, mat, B > { using type=mat,M,N>; enum {size=N, mm=0}; template static constexpr type impl(seq, F f, const mat & a, B b) { return {apply, B >::impl(make_seq<0,M>{}, f, getter{}(a), b )...}; } }; + template struct apply, A, mat> { using type=mat,M,N>; enum {size=N, mm=0}; template static constexpr type impl(seq, F f, A a, const mat & b) { return {apply>::impl(make_seq<0,M>{}, f, a, getter{}(b))...}; } }; + template struct apply, A...> { using type = ret_t; enum {size=0, mm=0}; static constexpr type impl(seq<>, F f, A... a) { return f(a...); } }; + + // Function objects for selecting between alternatives + struct min { template constexpr auto operator() (A a, B b) const -> typename std::remove_reference::type { return a constexpr auto operator() (A a, B b) const -> typename std::remove_reference::type { return a constexpr auto operator() (A a, B b, C c) const -> typename std::remove_reference::type { return a constexpr auto operator() (A a, B b, C c) const -> typename std::remove_reference::type { return a ? b : c; } }; + struct lerp { template constexpr auto operator() (A a, B b, C c) const -> decltype(a*(1-c) + b*c) { return a*(1-c) + b*c; } }; + + // Function objects for applying operators + struct op_pos { template constexpr auto operator() (A a) const -> decltype(+a) { return +a; } }; + struct op_neg { template constexpr auto operator() (A a) const -> decltype(-a) { return -a; } }; + struct op_not { template constexpr auto operator() (A a) const -> decltype(!a) { return !a; } }; + struct op_cmp { template constexpr auto operator() (A a) const -> decltype(~(a)) { return ~a; } }; + struct op_mul { template constexpr auto operator() (A a, B b) const -> decltype(a * b) { return a * b; } }; + struct op_div { template constexpr auto operator() (A a, B b) const -> decltype(a / b) { return a / b; } }; + struct op_mod { template constexpr auto operator() (A a, B b) const -> decltype(a % b) { return a % b; } }; + struct op_add { template constexpr auto operator() (A a, B b) const -> decltype(a + b) { return a + b; } }; + struct op_sub { template constexpr auto operator() (A a, B b) const -> decltype(a - b) { return a - b; } }; + struct op_lsh { template constexpr auto operator() (A a, B b) const -> decltype(a << b) { return a << b; } }; + struct op_rsh { template constexpr auto operator() (A a, B b) const -> decltype(a >> b) { return a >> b; } }; + struct op_lt { template constexpr auto operator() (A a, B b) const -> decltype(a < b) { return a < b; } }; + struct op_gt { template constexpr auto operator() (A a, B b) const -> decltype(a > b) { return a > b; } }; + struct op_le { template constexpr auto operator() (A a, B b) const -> decltype(a <= b) { return a <= b; } }; + struct op_ge { template constexpr auto operator() (A a, B b) const -> decltype(a >= b) { return a >= b; } }; + struct op_eq { template constexpr auto operator() (A a, B b) const -> decltype(a == b) { return a == b; } }; + struct op_ne { template constexpr auto operator() (A a, B b) const -> decltype(a != b) { return a != b; } }; + struct op_int { template constexpr auto operator() (A a, B b) const -> decltype(a & b) { return a & b; } }; + struct op_xor { template constexpr auto operator() (A a, B b) const -> decltype(a ^ b) { return a ^ b; } }; + struct op_un { template constexpr auto operator() (A a, B b) const -> decltype(a | b) { return a | b; } }; + struct op_and { template constexpr auto operator() (A a, B b) const -> decltype(a && b) { return a && b; } }; + struct op_or { template constexpr auto operator() (A a, B b) const -> decltype(a || b) { return a || b; } }; + + // Function objects for applying standard library math functions + struct std_abs { template auto operator() (A a) const -> decltype(std::abs (a)) { return std::abs (a); } }; + struct std_floor { template auto operator() (A a) const -> decltype(std::floor(a)) { return std::floor(a); } }; + struct std_ceil { template auto operator() (A a) const -> decltype(std::ceil (a)) { return std::ceil (a); } }; + struct std_exp { template auto operator() (A a) const -> decltype(std::exp (a)) { return std::exp (a); } }; + struct std_log { template auto operator() (A a) const -> decltype(std::log (a)) { return std::log (a); } }; + struct std_log10 { template auto operator() (A a) const -> decltype(std::log10(a)) { return std::log10(a); } }; + struct std_sqrt { template auto operator() (A a) const -> decltype(std::sqrt (a)) { return std::sqrt (a); } }; + struct std_sin { template auto operator() (A a) const -> decltype(std::sin (a)) { return std::sin (a); } }; + struct std_cos { template auto operator() (A a) const -> decltype(std::cos (a)) { return std::cos (a); } }; + struct std_tan { template auto operator() (A a) const -> decltype(std::tan (a)) { return std::tan (a); } }; + struct std_asin { template auto operator() (A a) const -> decltype(std::asin (a)) { return std::asin (a); } }; + struct std_acos { template auto operator() (A a) const -> decltype(std::acos (a)) { return std::acos (a); } }; + struct std_atan { template auto operator() (A a) const -> decltype(std::atan (a)) { return std::atan (a); } }; + struct std_sinh { template auto operator() (A a) const -> decltype(std::sinh (a)) { return std::sinh (a); } }; + struct std_cosh { template auto operator() (A a) const -> decltype(std::cosh (a)) { return std::cosh (a); } }; + struct std_tanh { template auto operator() (A a) const -> decltype(std::tanh (a)) { return std::tanh (a); } }; + struct std_round { template auto operator() (A a) const -> decltype(std::round(a)) { return std::round(a); } }; + struct std_fmod { template auto operator() (A a, B b) const -> decltype(std::fmod (a, b)) { return std::fmod (a, b); } }; + struct std_pow { template auto operator() (A a, B b) const -> decltype(std::pow (a, b)) { return std::pow (a, b); } }; + struct std_atan2 { template auto operator() (A a, B b) const -> decltype(std::atan2 (a, b)) { return std::atan2 (a, b); } }; + } + + // Small, fixed-length vector type, consisting of exactly M elements of type T, and presumed to be a column-vector unless otherwise noted + template struct vec + { + T x; + constexpr vec() : x() {} + constexpr vec(const T & x_) : x(x_) {} + // NOTE: vec does NOT have a constructor from pointer, this can conflict with initializing its single element from zero + template + constexpr explicit vec(const vec & v) : vec(static_cast(v.x)) {} + constexpr const T & operator[] (int i [[maybe_unused]]) const { return x; } + constexpr T & operator[] (int i [[maybe_unused]]) { return x; } + + template> constexpr vec(const U & u) : vec(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + template struct vec + { + T x,y; + constexpr vec() : x(), y() {} + constexpr vec(const T & x_, const T & y_) : x(x_), y(y_) {} + constexpr explicit vec(const T & s) : vec(s, s) {} + constexpr explicit vec(const T * p) : vec(p[0], p[1]) {} + template + constexpr explicit vec(const vec & v) : vec(static_cast(v.x), static_cast(v.y)) {} + constexpr const T & operator[] (int i) const { return i==0?x:y; } + constexpr T & operator[] (int i) { return i==0?x:y; } + + template> constexpr vec(const U & u) : vec(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + template struct vec + { + T x,y,z; + constexpr vec() : x(), y(), z() {} + constexpr vec(const T & x_, const T & y_, + const T & z_) : x(x_), y(y_), z(z_) {} + constexpr vec(const vec & xy, + const T & z_) : vec(xy.x, xy.y, z_) {} + constexpr explicit vec(const T & s) : vec(s, s, s) {} + constexpr explicit vec(const T * p) : vec(p[0], p[1], p[2]) {} + template + constexpr explicit vec(const vec & v) : vec(static_cast(v.x), static_cast(v.y), static_cast(v.z)) {} + constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:z; } + constexpr T & operator[] (int i) { return i==0?x:i==1?y:z; } + constexpr const vec & xy() const { return *reinterpret_cast *>(this); } + vec & xy() { return *reinterpret_cast *>(this); } + + template> constexpr vec(const U & u) : vec(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + template struct vec + { + T x,y,z,w; + constexpr vec() : x(), y(), z(), w() {} + constexpr vec(const T & x_, const T & y_, + const T & z_, const T & w_) : x(x_), y(y_), z(z_), w(w_) {} + constexpr vec(const vec & xy, + const T & z_, const T & w_) : vec(xy.x, xy.y, z_, w_) {} + constexpr vec(const vec & xyz, + const T & w_) : vec(xyz.x, xyz.y, xyz.z, w_) {} + constexpr explicit vec(const T & s) : vec(s, s, s, s) {} + constexpr explicit vec(const T * p) : vec(p[0], p[1], p[2], p[3]) {} + template + constexpr explicit vec(const vec & v) : vec(static_cast(v.x), static_cast(v.y), static_cast(v.z), static_cast(v.w)) {} + constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:i==2?z:w; } + constexpr T & operator[] (int i) { return i==0?x:i==1?y:i==2?z:w; } + constexpr const vec & xy() const { return *reinterpret_cast *>(this); } + constexpr const vec & xyz() const { return *reinterpret_cast *>(this); } + vec & xy() { return *reinterpret_cast *>(this); } + vec & xyz() { return *reinterpret_cast *>(this); } + + template> constexpr vec(const U & u) : vec(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + + // Small, fixed-size matrix type, consisting of exactly M rows and N columns of type T, stored in column-major order. + template struct mat + { + typedef vec V; + V x; + constexpr mat() : x() {} + constexpr mat(const V & x_) : x(x_) {} + constexpr explicit mat(const T & s) : x(s) {} + constexpr explicit mat(const T * p) : x(p+M*0) {} + template + constexpr explicit mat(const mat & m) : mat(V(m.x)) {} + constexpr vec row(int i) const { return {x[i]}; } + constexpr const V & operator[] (int j [[maybe_unused]]) const { return x; } + constexpr V & operator[] (int j [[maybe_unused]]) { return x; } + + template> constexpr mat(const U & u) : mat(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + template struct mat + { + typedef vec V; + V x,y; + constexpr mat() : x(), y() {} + constexpr mat(const V & x_, const V & y_) : x(x_), y(y_) {} + constexpr explicit mat(const T & s) : x(s), y(s) {} + constexpr explicit mat(const T * p) : x(p+M*0), y(p+M*1) {} + template + constexpr explicit mat(const mat & m) : mat(V(m.x), V(m.y)) {} + constexpr vec row(int i) const { return {x[i], y[i]}; } + constexpr const V & operator[] (int j) const { return j==0?x:y; } + constexpr V & operator[] (int j) { return j==0?x:y; } + + template> constexpr mat(const U & u) : mat(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + template struct mat + { + typedef vec V; + V x,y,z; + constexpr mat() : x(), y(), z() {} + constexpr mat(const V & x_, const V & y_, + const V & z_) : x(x_), y(y_), z(z_) {} + constexpr explicit mat(const T & s) : x(s), y(s), z(s) {} + constexpr explicit mat(const T * p) : x(p+M*0), y(p+M*1), z(p+M*2) {} + template + constexpr explicit mat(const mat & m) : mat(V(m.x), V(m.y), V(m.z)) {} + constexpr vec row(int i) const { return {x[i], y[i], z[i]}; } + constexpr const V & operator[] (int j) const { return j==0?x:j==1?y:z; } + constexpr V & operator[] (int j) { return j==0?x:j==1?y:z; } + + template> constexpr mat(const U & u) : mat(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + template struct mat + { + typedef vec V; + V x,y,z,w; + constexpr mat() : x(), y(), z(), w() {} + constexpr mat(const V & x_, const V & y_, + const V & z_, const V & w_) : x(x_), y(y_), z(z_), w(w_) {} + constexpr explicit mat(const T & s) : x(s), y(s), z(s), w(s) {} + constexpr explicit mat(const T * p) : x(p+M*0), y(p+M*1), z(p+M*2), w(p+M*3) {} + template + constexpr explicit mat(const mat & m) : mat(V(m.x), V(m.y), V(m.z), V(m.w)) {} + constexpr vec row(int i) const { return {x[i], y[i], z[i], w[i]}; } + constexpr const V & operator[] (int j) const { return j==0?x:j==1?y:j==2?z:w; } + constexpr V & operator[] (int j) { return j==0?x:j==1?y:j==2?z:w; } + + template> constexpr mat(const U & u) : mat(converter{}(u)) {} + template> constexpr operator U () const { return converter{}(*this); } + }; + + // Define a type which will convert to the multiplicative identity of any square matrix + struct identity_t { constexpr explicit identity_t(int) {} }; + template struct converter, identity_t> { constexpr mat operator() (identity_t) const { return {vec{1}}; } }; + template struct converter, identity_t> { constexpr mat operator() (identity_t) const { return {{1,0},{0,1}}; } }; + template struct converter, identity_t> { constexpr mat operator() (identity_t) const { return {{1,0,0},{0,1,0},{0,0,1}}; } }; + template struct converter, identity_t> { constexpr mat operator() (identity_t) const { return {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; } }; + constexpr identity_t identity {1}; + + // Produce a scalar by applying f(A,B) -> A to adjacent pairs of elements from a vec/mat in left-to-right/column-major order (matching the associativity of arithmetic and logical operators) + template constexpr A fold(F f, A a, const vec & b) { return f(a, b.x); } + template constexpr A fold(F f, A a, const vec & b) { return f(f(a, b.x), b.y); } + template constexpr A fold(F f, A a, const vec & b) { return f(f(f(a, b.x), b.y), b.z); } + template constexpr A fold(F f, A a, const vec & b) { return f(f(f(f(a, b.x), b.y), b.z), b.w); } + template constexpr A fold(F f, A a, const mat & b) { return fold(f, a, b.x); } + template constexpr A fold(F f, A a, const mat & b) { return fold(f, fold(f, a, b.x), b.y); } + template constexpr A fold(F f, A a, const mat & b) { return fold(f, fold(f, fold(f, a, b.x), b.y), b.z); } + template constexpr A fold(F f, A a, const mat & b) { return fold(f, fold(f, fold(f, fold(f, a, b.x), b.y), b.z), b.w); } + + // Type aliases for the result of calling apply(...) with various arguments, can be used with return type SFINAE to constrian overload sets + template using apply_t = typename detail::apply::type; + template using mm_apply_t = typename std::enable_if::mm, apply_t>::type; + template using no_mm_apply_t = typename std::enable_if::mm, apply_t>::type; + template using scalar_t = typename detail::scalar_type::type; // Underlying scalar type when performing elementwise operations + + // apply(f,...) applies the provided function in an elementwise fashion to its arguments, producing an object of the same dimensions + template constexpr apply_t apply(F func, const A & ... args) { return detail::apply::impl(detail::make_seq<0,detail::apply::size>{}, func, args...); } + + // map(a,f) is equivalent to apply(f,a) + template constexpr apply_t map(const A & a, F func) { return apply(func, a); } + + // zip(a,b,f) is equivalent to apply(f,a,b) + template constexpr apply_t zip(const A & a, const B & b, F func) { return apply(func, a, b); } + + // Relational operators are defined to compare the elements of two vectors or matrices lexicographically, in column-major order + template constexpr typename detail::any_compare::type compare(const A & a, const B & b) { return detail::any_compare()(a,b); } + template constexpr auto operator == (const A & a, const B & b) -> decltype(compare(a,b) == 0) { return compare(a,b) == 0; } + template constexpr auto operator != (const A & a, const B & b) -> decltype(compare(a,b) != 0) { return compare(a,b) != 0; } + template constexpr auto operator < (const A & a, const B & b) -> decltype(compare(a,b) < 0) { return compare(a,b) < 0; } + template constexpr auto operator > (const A & a, const B & b) -> decltype(compare(a,b) > 0) { return compare(a,b) > 0; } + template constexpr auto operator <= (const A & a, const B & b) -> decltype(compare(a,b) <= 0) { return compare(a,b) <= 0; } + template constexpr auto operator >= (const A & a, const B & b) -> decltype(compare(a,b) >= 0) { return compare(a,b) >= 0; } + + // Functions for coalescing scalar values + template constexpr bool any (const A & a) { return fold(detail::op_or{}, false, a); } + template constexpr bool all (const A & a) { return fold(detail::op_and{}, true, a); } + template constexpr scalar_t sum (const A & a) { return fold(detail::op_add{}, scalar_t(0), a); } + template constexpr scalar_t product(const A & a) { return fold(detail::op_mul{}, scalar_t(1), a); } + template constexpr scalar_t minelem(const A & a) { return fold(detail::min{}, a.x, a); } + template constexpr scalar_t maxelem(const A & a) { return fold(detail::max{}, a.x, a); } + template int argmin(const vec & a) { int j=0; for(int i=1; i int argmax(const vec & a) { int j=0; for(int i=1; i a[j]) j = i; return j; } + + // Unary operators are defined component-wise for linalg types + template constexpr apply_t operator + (const A & a) { return apply(detail::op_pos{}, a); } + template constexpr apply_t operator - (const A & a) { return apply(detail::op_neg{}, a); } + template constexpr apply_t operator ~ (const A & a) { return apply(detail::op_cmp{}, a); } + template constexpr apply_t operator ! (const A & a) { return apply(detail::op_not{}, a); } + + // Binary operators are defined component-wise for linalg types, EXCEPT for `operator *` + template constexpr apply_t operator + (const A & a, const B & b) { return apply(detail::op_add{}, a, b); } + template constexpr apply_t operator - (const A & a, const B & b) { return apply(detail::op_sub{}, a, b); } + template constexpr apply_t cmul (const A & a, const B & b) { return apply(detail::op_mul{}, a, b); } + template constexpr apply_t operator / (const A & a, const B & b) { return apply(detail::op_div{}, a, b); } + template constexpr apply_t operator % (const A & a, const B & b) { return apply(detail::op_mod{}, a, b); } + template constexpr apply_t operator | (const A & a, const B & b) { return apply(detail::op_un{}, a, b); } + template constexpr apply_t operator ^ (const A & a, const B & b) { return apply(detail::op_xor{}, a, b); } + template constexpr apply_t operator & (const A & a, const B & b) { return apply(detail::op_int{}, a, b); } + template constexpr apply_t operator << (const A & a, const B & b) { return apply(detail::op_lsh{}, a, b); } + template constexpr apply_t operator >> (const A & a, const B & b) { return apply(detail::op_rsh{}, a, b); } + + // Binary `operator *` was originally defined component-wise for all patterns, in a fashion consistent with the other operators. However, + // this was one of the most frequent sources of confusion among new users of this library, with the binary `operator *` being used accidentally + // by users who INTENDED the semantics of the algebraic matrix product, but RECEIVED the semantics of the Hadamard product. While there is + // precedent within the HLSL, Fortran, R, APL, J, and Mathematica programming languages for `operator *` having the semantics of the Hadamard + // product between matrices, it is counterintuitive to users of GLSL, Eigen, and many other languages and libraries that chose matrix product + // semantics for `operator *`. + // + // For these reasons, binary `operator *` is now DEPRECATED between pairs of matrices. Users may call `cmul(...)` for component-wise multiplication, + // or `mul(...)` for matrix multiplication. Binary `operator *` continues to be available for vector * vector, vector * scalar, matrix * scalar, etc. + template constexpr no_mm_apply_t operator * (const A & a, const B & b) { return cmul(a,b); } + #ifndef LINALG_FORWARD_COMPATIBLE + template [[deprecated("`operator *` between pairs of matrices is deprecated. See the source text for details.")]] constexpr mm_apply_t operator * (const A & a, const B & b) { return cmul(a,b); } + #endif + + // Binary assignment operators a $= b is always defined as though it were explicitly written a = a $ b + template constexpr auto operator += (A & a, const B & b) -> decltype(a = a + b) { return a = a + b; } + template constexpr auto operator -= (A & a, const B & b) -> decltype(a = a - b) { return a = a - b; } + template constexpr auto operator *= (A & a, const B & b) -> decltype(a = a * b) { return a = a * b; } + template constexpr auto operator /= (A & a, const B & b) -> decltype(a = a / b) { return a = a / b; } + template constexpr auto operator %= (A & a, const B & b) -> decltype(a = a % b) { return a = a % b; } + template constexpr auto operator |= (A & a, const B & b) -> decltype(a = a | b) { return a = a | b; } + template constexpr auto operator ^= (A & a, const B & b) -> decltype(a = a ^ b) { return a = a ^ b; } + template constexpr auto operator &= (A & a, const B & b) -> decltype(a = a & b) { return a = a & b; } + template constexpr auto operator <<= (A & a, const B & b) -> decltype(a = a << b) { return a = a << b; } + template constexpr auto operator >>= (A & a, const B & b) -> decltype(a = a >> b) { return a = a >> b; } + + // Swizzles and subobjects + template constexpr vec swizzle(const vec & a) { return {detail::getter{}(a)...}; } + template constexpr vec subvec (const vec & a) { return detail::swizzle(a, detail::make_seq{}); } + template constexpr mat submat (const mat & a) { return detail::swizzle(a, detail::make_seq{}, detail::make_seq{}); } + + // Component-wise standard library math functions + template apply_t abs (const A & a) { return apply(detail::std_abs{}, a); } + template apply_t floor(const A & a) { return apply(detail::std_floor{}, a); } + template apply_t ceil (const A & a) { return apply(detail::std_ceil{}, a); } + template apply_t exp (const A & a) { return apply(detail::std_exp{}, a); } + template apply_t log (const A & a) { return apply(detail::std_log{}, a); } + template apply_t log10(const A & a) { return apply(detail::std_log10{}, a); } + template apply_t sqrt (const A & a) { return apply(detail::std_sqrt{}, a); } + template apply_t sin (const A & a) { return apply(detail::std_sin{}, a); } + template apply_t cos (const A & a) { return apply(detail::std_cos{}, a); } + template apply_t tan (const A & a) { return apply(detail::std_tan{}, a); } + template apply_t asin (const A & a) { return apply(detail::std_asin{}, a); } + template apply_t acos (const A & a) { return apply(detail::std_acos{}, a); } + template apply_t atan (const A & a) { return apply(detail::std_atan{}, a); } + template apply_t sinh (const A & a) { return apply(detail::std_sinh{}, a); } + template apply_t cosh (const A & a) { return apply(detail::std_cosh{}, a); } + template apply_t tanh (const A & a) { return apply(detail::std_tanh{}, a); } + template apply_t round(const A & a) { return apply(detail::std_round{}, a); } + + template apply_t fmod (const A & a, const B & b) { return apply(detail::std_fmod{}, a, b); } + template apply_t pow (const A & a, const B & b) { return apply(detail::std_pow{}, a, b); } + template apply_t atan2 (const A & a, const B & b) { return apply(detail::std_atan2{}, a, b); } + + // Component-wise relational functions on vectors + template constexpr apply_t equal (const A & a, const B & b) { return apply(detail::op_eq{}, a, b); } + template constexpr apply_t nequal (const A & a, const B & b) { return apply(detail::op_ne{}, a, b); } + template constexpr apply_t less (const A & a, const B & b) { return apply(detail::op_lt{}, a, b); } + template constexpr apply_t greater(const A & a, const B & b) { return apply(detail::op_gt{}, a, b); } + template constexpr apply_t lequal (const A & a, const B & b) { return apply(detail::op_le{}, a, b); } + template constexpr apply_t gequal (const A & a, const B & b) { return apply(detail::op_ge{}, a, b); } + + // Component-wise selection functions on vectors + template constexpr apply_t min(const A & a, const B & b) { return apply(detail::min{}, a, b); } + template constexpr apply_t max(const A & a, const B & b) { return apply(detail::max{}, a, b); } + template constexpr apply_t clamp (const X & x, const L & l, const H & h) { return apply(detail::clamp{}, x, l, h); } + template constexpr apply_t select(const P & p, const A & a, const B & b) { return apply(detail::select{}, p, a, b); } + template constexpr apply_t lerp (const A & a, const B & b, const T & t) { return apply(detail::lerp{}, a, b, t); } + + // Support for vector algebra + template constexpr T cross (const vec & a, const vec & b) { return a.x*b.y-a.y*b.x; } + template constexpr vec cross (T a, const vec & b) { return {-a*b.y, a*b.x}; } + template constexpr vec cross (const vec & a, T b) { return {a.y*b, -a.x*b}; } + template constexpr vec cross (const vec & a, const vec & b) { return {a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x}; } + template constexpr T dot (const vec & a, const vec & b) { return sum(a*b); } + template constexpr T length2 (const vec & a) { return dot(a,a); } + template T length (const vec & a) { return std::sqrt(length2(a)); } + template vec normalize(const vec & a) { return a / length(a); } + template constexpr T distance2(const vec & a, const vec & b) { return length2(b-a); } + template T distance (const vec & a, const vec & b) { return length(b-a); } + template T uangle (const vec & a, const vec & b) { T d=dot(a,b); return d > 1 ? 0 : std::acos(d < -1 ? -1 : d); } + template T angle (const vec & a, const vec & b) { return uangle(normalize(a), normalize(b)); } + template vec rot (T a, const vec & v) { const T s = std::sin(a), c = std::cos(a); return {v.x*c - v.y*s, v.x*s + v.y*c}; } + template vec nlerp (const vec & a, const vec & b, T t) { return normalize(lerp(a,b,t)); } + template vec slerp (const vec & a, const vec & b, T t) { T th=uangle(a,b); return th == 0 ? a : a*(std::sin(th*(1-t))/std::sin(th)) + b*(std::sin(th*t)/std::sin(th)); } + + // Support for quaternion algebra using 4D vectors, representing xi + yj + zk + w + template constexpr vec qconj(const vec & q) { return {-q.x,-q.y,-q.z,q.w}; } + template vec qinv (const vec & q) { return qconj(q)/length2(q); } + template vec qexp (const vec & q) { const auto v = q.xyz(); const auto vv = length(v); return std::exp(q.w) * vec{v * (vv > 0 ? std::sin(vv)/vv : 0), std::cos(vv)}; } + template vec qlog (const vec & q) { const auto v = q.xyz(); const auto vv = length(v), qq = length(q); return {v * (vv > 0 ? std::acos(q.w/qq)/vv : 0), std::log(qq)}; } + template vec qpow (const vec & q, const T & p) { const auto v = q.xyz(); const auto vv = length(v), qq = length(q), th = std::acos(q.w/qq); return std::pow(qq,p)*vec{v * (vv > 0 ? std::sin(p*th)/vv : 0), std::cos(p*th)}; } + template constexpr vec qmul (const vec & a, const vec & b) { return {a.x*b.w+a.w*b.x+a.y*b.z-a.z*b.y, a.y*b.w+a.w*b.y+a.z*b.x-a.x*b.z, a.z*b.w+a.w*b.z+a.x*b.y-a.y*b.x, a.w*b.w-a.x*b.x-a.y*b.y-a.z*b.z}; } + template constexpr vec qmul(const vec & a, R... r) { return qmul(a, qmul(r...)); } + + // Support for 3D spatial rotations using quaternions, via qmul(qmul(q, v), qconj(q)) + template constexpr vec qxdir (const vec & q) { return {q.w*q.w+q.x*q.x-q.y*q.y-q.z*q.z, (q.x*q.y+q.z*q.w)*2, (q.z*q.x-q.y*q.w)*2}; } + template constexpr vec qydir (const vec & q) { return {(q.x*q.y-q.z*q.w)*2, q.w*q.w-q.x*q.x+q.y*q.y-q.z*q.z, (q.y*q.z+q.x*q.w)*2}; } + template constexpr vec qzdir (const vec & q) { return {(q.z*q.x+q.y*q.w)*2, (q.y*q.z-q.x*q.w)*2, q.w*q.w-q.x*q.x-q.y*q.y+q.z*q.z}; } + template constexpr mat qmat (const vec & q) { return {qxdir(q), qydir(q), qzdir(q)}; } + template constexpr vec qrot (const vec & q, const vec & v) { return qxdir(q)*v.x + qydir(q)*v.y + qzdir(q)*v.z; } + template T qangle(const vec & q) { return std::atan2(length(q.xyz()), q.w)*2; } + template vec qaxis (const vec & q) { return normalize(q.xyz()); } + template vec qnlerp(const vec & a, const vec & b, T t) { return nlerp(a, dot(a,b) < 0 ? -b : b, t); } + template vec qslerp(const vec & a, const vec & b, T t) { return slerp(a, dot(a,b) < 0 ? -b : b, t); } + + // Support for matrix algebra + template constexpr vec mul(const mat & a, const vec & b) { return a.x*b.x; } + template constexpr vec mul(const mat & a, const vec & b) { return a.x*b.x + a.y*b.y; } + template constexpr vec mul(const mat & a, const vec & b) { return a.x*b.x + a.y*b.y + a.z*b.z; } + template constexpr vec mul(const mat & a, const vec & b) { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; } + template constexpr mat mul(const mat & a, const mat & b) { return {mul(a,b.x)}; } + template constexpr mat mul(const mat & a, const mat & b) { return {mul(a,b.x), mul(a,b.y)}; } + template constexpr mat mul(const mat & a, const mat & b) { return {mul(a,b.x), mul(a,b.y), mul(a,b.z)}; } + template constexpr mat mul(const mat & a, const mat & b) { return {mul(a,b.x), mul(a,b.y), mul(a,b.z), mul(a,b.w)}; } + template constexpr vec mul(const mat & a, const mat & b, const vec & c) { return mul(mul(a,b),c); } + template constexpr mat mul(const mat & a, const mat & b, const mat & c) { return mul(mul(a,b),c); } + template constexpr vec mul(const mat & a, const mat & b, const mat & c, const vec & d) { return mul(mul(a,b,c),d); } + template constexpr mat mul(const mat & a, const mat & b, const mat & c, const mat & d) { return mul(mul(a,b,c),d); } + template constexpr mat outerprod(const vec & a, const vec & b) { return {a*b.x}; } + template constexpr mat outerprod(const vec & a, const vec & b) { return {a*b.x, a*b.y}; } + template constexpr mat outerprod(const vec & a, const vec & b) { return {a*b.x, a*b.y, a*b.z}; } + template constexpr mat outerprod(const vec & a, const vec & b) { return {a*b.x, a*b.y, a*b.z, a*b.w}; } + template constexpr vec diagonal(const mat & a) { return {a.x.x}; } + template constexpr vec diagonal(const mat & a) { return {a.x.x, a.y.y}; } + template constexpr vec diagonal(const mat & a) { return {a.x.x, a.y.y, a.z.z}; } + template constexpr vec diagonal(const mat & a) { return {a.x.x, a.y.y, a.z.z, a.w.w}; } + template constexpr T trace(const mat & a) { return sum(diagonal(a)); } + template constexpr mat transpose(const mat & m) { return {m.row(0)}; } + template constexpr mat transpose(const mat & m) { return {m.row(0), m.row(1)}; } + template constexpr mat transpose(const mat & m) { return {m.row(0), m.row(1), m.row(2)}; } + template constexpr mat transpose(const mat & m) { return {m.row(0), m.row(1), m.row(2), m.row(3)}; } + template constexpr mat transpose(const vec & m) { return transpose(mat(m)); } + template constexpr mat adjugate(const mat & a [[maybe_unused]]) { return {vec{1}}; } + template constexpr mat adjugate(const mat & a) { return {{a.y.y, -a.x.y}, {-a.y.x, a.x.x}}; } + template constexpr mat adjugate(const mat & a); + template constexpr mat adjugate(const mat & a); + template constexpr mat comatrix(const mat & a) { return transpose(adjugate(a)); } + template constexpr T determinant(const mat & a) { return a.x.x; } + template constexpr T determinant(const mat & a) { return a.x.x*a.y.y - a.x.y*a.y.x; } + template constexpr T determinant(const mat & a) { return a.x.x*(a.y.y*a.z.z - a.z.y*a.y.z) + a.x.y*(a.y.z*a.z.x - a.z.z*a.y.x) + a.x.z*(a.y.x*a.z.y - a.z.x*a.y.y); } + template constexpr T determinant(const mat & a); + template constexpr mat inverse(const mat & a) { return adjugate(a)/determinant(a); } + + // Vectors and matrices can be used as ranges + template T * begin( vec & a) { return &a.x; } + template const T * begin(const vec & a) { return &a.x; } + template T * end ( vec & a) { return begin(a) + M; } + template const T * end (const vec & a) { return begin(a) + M; } + template vec * begin( mat & a) { return &a.x; } + template const vec * begin(const mat & a) { return &a.x; } + template vec * end ( mat & a) { return begin(a) + N; } + template const vec * end (const mat & a) { return begin(a) + N; } + + // Factory functions for 3D spatial transformations (will possibly be removed or changed in a future version) + enum fwd_axis { neg_z, pos_z }; // Should projection matrices be generated assuming forward is {0,0,-1} or {0,0,1} + enum z_range { neg_one_to_one, zero_to_one }; // Should projection matrices map z into the range of [-1,1] or [0,1]? + template vec rotation_quat (const vec & axis, T angle) { return {axis*std::sin(angle/2), std::cos(angle/2)}; } + template vec rotation_quat (const mat & m); + template mat translation_matrix(const vec & translation) { return {{1,0,0,0},{0,1,0,0},{0,0,1,0},{translation,1}}; } + template mat rotation_matrix (const vec & rotation) { return {{qxdir(rotation),0}, {qydir(rotation),0}, {qzdir(rotation),0}, {0,0,0,1}}; } + template mat scaling_matrix (const vec & scaling) { return {{scaling.x,0,0,0}, {0,scaling.y,0,0}, {0,0,scaling.z,0}, {0,0,0,1}}; } + template mat pose_matrix (const vec & q, const vec & p) { return {{qxdir(q),0}, {qydir(q),0}, {qzdir(q),0}, {p,1}}; } + template mat lookat_matrix (const vec & eye, const vec & center, const vec & view_y_dir, fwd_axis fwd = neg_z); + template mat frustum_matrix (T x0, T x1, T y0, T y1, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one); + template mat perspective_matrix(T fovy, T aspect, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one) { T y = n*std::tan(fovy / 2), x = y*aspect; return frustum_matrix(-x, x, -y, y, n, f, a, z); } + + // Provide typedefs for common element types and vector/matrix sizes + namespace aliases + { + typedef vec bool1; typedef vec byte1; typedef vec short1; typedef vec ushort1; + typedef vec bool2; typedef vec byte2; typedef vec short2; typedef vec ushort2; + typedef vec bool3; typedef vec byte3; typedef vec short3; typedef vec ushort3; + typedef vec bool4; typedef vec byte4; typedef vec short4; typedef vec ushort4; + typedef vec int1; typedef vec uint1; typedef vec float1; typedef vec double1; + typedef vec int2; typedef vec uint2; typedef vec float2; typedef vec double2; + typedef vec int3; typedef vec uint3; typedef vec float3; typedef vec double3; + typedef vec int4; typedef vec uint4; typedef vec float4; typedef vec double4; + typedef mat bool1x1; typedef mat int1x1; typedef mat float1x1; typedef mat double1x1; + typedef mat bool1x2; typedef mat int1x2; typedef mat float1x2; typedef mat double1x2; + typedef mat bool1x3; typedef mat int1x3; typedef mat float1x3; typedef mat double1x3; + typedef mat bool1x4; typedef mat int1x4; typedef mat float1x4; typedef mat double1x4; + typedef mat bool2x1; typedef mat int2x1; typedef mat float2x1; typedef mat double2x1; + typedef mat bool2x2; typedef mat int2x2; typedef mat float2x2; typedef mat double2x2; + typedef mat bool2x3; typedef mat int2x3; typedef mat float2x3; typedef mat double2x3; + typedef mat bool2x4; typedef mat int2x4; typedef mat float2x4; typedef mat double2x4; + typedef mat bool3x1; typedef mat int3x1; typedef mat float3x1; typedef mat double3x1; + typedef mat bool3x2; typedef mat int3x2; typedef mat float3x2; typedef mat double3x2; + typedef mat bool3x3; typedef mat int3x3; typedef mat float3x3; typedef mat double3x3; + typedef mat bool3x4; typedef mat int3x4; typedef mat float3x4; typedef mat double3x4; + typedef mat bool4x1; typedef mat int4x1; typedef mat float4x1; typedef mat double4x1; + typedef mat bool4x2; typedef mat int4x2; typedef mat float4x2; typedef mat double4x2; + typedef mat bool4x3; typedef mat int4x3; typedef mat float4x3; typedef mat double4x3; + typedef mat bool4x4; typedef mat int4x4; typedef mat float4x4; typedef mat double4x4; + } +} + +// Definitions of functions too long to be defined inline +template constexpr linalg::mat linalg::adjugate(const mat & a) +{ + return {{a.y.y*a.z.z - a.z.y*a.y.z, a.z.y*a.x.z - a.x.y*a.z.z, a.x.y*a.y.z - a.y.y*a.x.z}, + {a.y.z*a.z.x - a.z.z*a.y.x, a.z.z*a.x.x - a.x.z*a.z.x, a.x.z*a.y.x - a.y.z*a.x.x}, + {a.y.x*a.z.y - a.z.x*a.y.y, a.z.x*a.x.y - a.x.x*a.z.y, a.x.x*a.y.y - a.y.x*a.x.y}}; +} + +template constexpr linalg::mat linalg::adjugate(const mat & a) +{ + return {{a.y.y*a.z.z*a.w.w + a.w.y*a.y.z*a.z.w + a.z.y*a.w.z*a.y.w - a.y.y*a.w.z*a.z.w - a.z.y*a.y.z*a.w.w - a.w.y*a.z.z*a.y.w, + a.x.y*a.w.z*a.z.w + a.z.y*a.x.z*a.w.w + a.w.y*a.z.z*a.x.w - a.w.y*a.x.z*a.z.w - a.z.y*a.w.z*a.x.w - a.x.y*a.z.z*a.w.w, + a.x.y*a.y.z*a.w.w + a.w.y*a.x.z*a.y.w + a.y.y*a.w.z*a.x.w - a.x.y*a.w.z*a.y.w - a.y.y*a.x.z*a.w.w - a.w.y*a.y.z*a.x.w, + a.x.y*a.z.z*a.y.w + a.y.y*a.x.z*a.z.w + a.z.y*a.y.z*a.x.w - a.x.y*a.y.z*a.z.w - a.z.y*a.x.z*a.y.w - a.y.y*a.z.z*a.x.w}, + {a.y.z*a.w.w*a.z.x + a.z.z*a.y.w*a.w.x + a.w.z*a.z.w*a.y.x - a.y.z*a.z.w*a.w.x - a.w.z*a.y.w*a.z.x - a.z.z*a.w.w*a.y.x, + a.x.z*a.z.w*a.w.x + a.w.z*a.x.w*a.z.x + a.z.z*a.w.w*a.x.x - a.x.z*a.w.w*a.z.x - a.z.z*a.x.w*a.w.x - a.w.z*a.z.w*a.x.x, + a.x.z*a.w.w*a.y.x + a.y.z*a.x.w*a.w.x + a.w.z*a.y.w*a.x.x - a.x.z*a.y.w*a.w.x - a.w.z*a.x.w*a.y.x - a.y.z*a.w.w*a.x.x, + a.x.z*a.y.w*a.z.x + a.z.z*a.x.w*a.y.x + a.y.z*a.z.w*a.x.x - a.x.z*a.z.w*a.y.x - a.y.z*a.x.w*a.z.x - a.z.z*a.y.w*a.x.x}, + {a.y.w*a.z.x*a.w.y + a.w.w*a.y.x*a.z.y + a.z.w*a.w.x*a.y.y - a.y.w*a.w.x*a.z.y - a.z.w*a.y.x*a.w.y - a.w.w*a.z.x*a.y.y, + a.x.w*a.w.x*a.z.y + a.z.w*a.x.x*a.w.y + a.w.w*a.z.x*a.x.y - a.x.w*a.z.x*a.w.y - a.w.w*a.x.x*a.z.y - a.z.w*a.w.x*a.x.y, + a.x.w*a.y.x*a.w.y + a.w.w*a.x.x*a.y.y + a.y.w*a.w.x*a.x.y - a.x.w*a.w.x*a.y.y - a.y.w*a.x.x*a.w.y - a.w.w*a.y.x*a.x.y, + a.x.w*a.z.x*a.y.y + a.y.w*a.x.x*a.z.y + a.z.w*a.y.x*a.x.y - a.x.w*a.y.x*a.z.y - a.z.w*a.x.x*a.y.y - a.y.w*a.z.x*a.x.y}, + {a.y.x*a.w.y*a.z.z + a.z.x*a.y.y*a.w.z + a.w.x*a.z.y*a.y.z - a.y.x*a.z.y*a.w.z - a.w.x*a.y.y*a.z.z - a.z.x*a.w.y*a.y.z, + a.x.x*a.z.y*a.w.z + a.w.x*a.x.y*a.z.z + a.z.x*a.w.y*a.x.z - a.x.x*a.w.y*a.z.z - a.z.x*a.x.y*a.w.z - a.w.x*a.z.y*a.x.z, + a.x.x*a.w.y*a.y.z + a.y.x*a.x.y*a.w.z + a.w.x*a.y.y*a.x.z - a.x.x*a.y.y*a.w.z - a.w.x*a.x.y*a.y.z - a.y.x*a.w.y*a.x.z, + a.x.x*a.y.y*a.z.z + a.z.x*a.x.y*a.y.z + a.y.x*a.z.y*a.x.z - a.x.x*a.z.y*a.y.z - a.y.x*a.x.y*a.z.z - a.z.x*a.y.y*a.x.z}}; +} + +template constexpr T linalg::determinant(const mat & a) +{ + return a.x.x*(a.y.y*a.z.z*a.w.w + a.w.y*a.y.z*a.z.w + a.z.y*a.w.z*a.y.w - a.y.y*a.w.z*a.z.w - a.z.y*a.y.z*a.w.w - a.w.y*a.z.z*a.y.w) + + a.x.y*(a.y.z*a.w.w*a.z.x + a.z.z*a.y.w*a.w.x + a.w.z*a.z.w*a.y.x - a.y.z*a.z.w*a.w.x - a.w.z*a.y.w*a.z.x - a.z.z*a.w.w*a.y.x) + + a.x.z*(a.y.w*a.z.x*a.w.y + a.w.w*a.y.x*a.z.y + a.z.w*a.w.x*a.y.y - a.y.w*a.w.x*a.z.y - a.z.w*a.y.x*a.w.y - a.w.w*a.z.x*a.y.y) + + a.x.w*(a.y.x*a.w.y*a.z.z + a.z.x*a.y.y*a.w.z + a.w.x*a.z.y*a.y.z - a.y.x*a.z.y*a.w.z - a.w.x*a.y.y*a.z.z - a.z.x*a.w.y*a.y.z); +} + +template linalg::vec linalg::rotation_quat(const mat & m) +{ + const vec q {m.x.x-m.y.y-m.z.z, m.y.y-m.x.x-m.z.z, m.z.z-m.x.x-m.y.y, m.x.x+m.y.y+m.z.z}, s[] { + {1, m.x.y + m.y.x, m.z.x + m.x.z, m.y.z - m.z.y}, + {m.x.y + m.y.x, 1, m.y.z + m.z.y, m.z.x - m.x.z}, + {m.x.z + m.z.x, m.y.z + m.z.y, 1, m.x.y - m.y.x}, + {m.y.z - m.z.y, m.z.x - m.x.z, m.x.y - m.y.x, 1}}; + return copysign(normalize(sqrt(max(T(0), T(1)+q))), s[argmax(q)]); +} + +template linalg::mat linalg::lookat_matrix(const vec & eye, const vec & center, const vec & view_y_dir, fwd_axis a) +{ + const vec f = normalize(center - eye), z = a == pos_z ? f : -f, x = normalize(cross(view_y_dir, z)), y = cross(z, x); + return inverse(mat{{x,0},{y,0},{z,0},{eye,1}}); +} + +template linalg::mat linalg::frustum_matrix(T x0, T x1, T y0, T y1, T n, T f, fwd_axis a, z_range z) +{ + const T s = a == pos_z ? T(1) : T(-1), o = z == neg_one_to_one ? n : 0; + return {{2*n/(x1-x0),0,0,0}, {0,2*n/(y1-y0),0,0}, {-s*(x0+x1)/(x1-x0),-s*(y0+y1)/(y1-y0),s*(f+o)/(f-n),s}, {0,0,-(n+o)*f/(f-n),0}}; +} + +#endif \ No newline at end of file diff --git a/build/assets/include/math.h b/build/assets/include/math.h index 949f9e9..5e7c8d0 100644 --- a/build/assets/include/math.h +++ b/build/assets/include/math.h @@ -21,10 +21,32 @@ static inline bool isinf(double x) { return !isnan(x) && isnan(x - x); } -IMPORT(Math_sin) double sin(double x); -static inline double cos(double x) { - return sin(x + M_PI_2); -} +#define IMPORT_UNARY(fn) IMPORT(Math_##fn) double fn(double); +#define IMPORT_BINARY(fn) IMPORT(Math_##fn) double fn(double, double); + +IMPORT_UNARY(floor) +IMPORT_UNARY(ceil) +IMPORT_UNARY(round) +IMPORT_UNARY(exp) +IMPORT_UNARY(log) +IMPORT_UNARY(log10) +IMPORT_UNARY(sqrt) +IMPORT_UNARY(sin) +IMPORT_UNARY(cos) +IMPORT_UNARY(tan) +IMPORT_UNARY(asin) +IMPORT_UNARY(acos) +IMPORT_UNARY(atan) +IMPORT_UNARY(sinh) +IMPORT_UNARY(cosh) +IMPORT_UNARY(tanh) + +IMPORT_BINARY(fmod) +IMPORT_BINARY(pow) +IMPORT_BINARY(atan2) + +#undef IMPORT_UNARY +#undef IMPORT_BINARY #ifdef __cplusplus } diff --git a/build/assets/include/math.hpp b/build/assets/include/math.hpp new file mode 100644 index 0000000..db1f273 --- /dev/null +++ b/build/assets/include/math.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#undef abs +#undef max +#undef min + +namespace std { +template T max(T a, T b) { + return a < b ? b : a; +} +template T min(T a, T b) { + return a < b ? a : b; +} +template T abs(T a) { + return a < 0 ? -a : a; +} + +#define UNARY(fn) \ + template \ + typename std::enable_if::value, T>::type \ + fn(T a) { return static_cast(::fn(static_cast(a))); } + +#define BINARY(fn) \ + template \ + typename std::enable_if::value, T>::type \ + fn(T a, T b) { return static_cast(::fn(static_cast(a), static_cast(b))); } + +UNARY(floor) +UNARY(ceil) +UNARY(exp) +UNARY(log) +UNARY(log10) +UNARY(sqrt) +UNARY(sin) +UNARY(cos) +UNARY(tan) +UNARY(asin) +UNARY(acos) +UNARY(atan) +UNARY(sinh) +UNARY(cosh) +UNARY(tanh) +UNARY(round) + +BINARY(fmod) +BINARY(pow) +BINARY(atan2) + +#undef UNARY +#undef BINARY +} // namespace std + diff --git a/build/assets/include/stdlib.h b/build/assets/include/stdlib.h index 067c0a4..1c964b0 100644 --- a/build/assets/include/stdlib.h +++ b/build/assets/include/stdlib.h @@ -1,9 +1,5 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include #include #include @@ -11,6 +7,10 @@ extern "C" { #define RAND_MAX INT_MAX +#ifdef __cplusplus +extern "C" { +#endif + extern unsigned char __heap_base; #define IMPORT(name) __attribute__((import_module("env"), import_name(#name))) @@ -21,8 +21,16 @@ void* calloc(size_t n, size_t size); void* realloc(void* ptr, size_t size); void free(void* ptr); -void* memset(void* dest, uint8_t ch, size_t n); -void* memcpy(void* dest, const void* src, size_t n); +__attribute__((__always_inline__)) static inline +void* memset(void* d, int c, size_t n) { + return __builtin_memset(d, c, n); +} + +__attribute__((__always_inline__)) static inline +void* memcpy(void* dest, const void* src, size_t n) { + return __builtin_memcpy(dest, src, n); +} + int memcmp(const void* s1, const void* s2, size_t n); IMPORT(log) void printf(const char* format, ...); diff --git a/build/assets/include/type_traits.hpp b/build/assets/include/type_traits.hpp new file mode 100644 index 0000000..d11b241 --- /dev/null +++ b/build/assets/include/type_traits.hpp @@ -0,0 +1,140 @@ +#pragma once + +namespace std { + template + struct integral_constant + { + static constexpr T value = v; + using value_type = T; + using type = integral_constant; // using injected-class-name + constexpr operator value_type() const noexcept { return value; } + constexpr value_type operator()() const noexcept { return value; } // since c++14 + }; + + using true_type = std::integral_constant; + using false_type = std::integral_constant; + + template< bool B > + using bool_constant = integral_constant; + + template + struct enable_if {}; + + template + struct enable_if { typedef T type; }; + + template + struct is_same : std::false_type {}; + + template + struct is_same : std::true_type {}; + + namespace detail + { + template + struct type_identity { using type = T; }; // or use std::type_identity (since C++20) + + template // Note that “cv void&” is a substitution failure + auto try_add_lvalue_reference(int) -> type_identity; + template // Handle T = cv void case + auto try_add_lvalue_reference(...) -> type_identity; + + template + auto try_add_rvalue_reference(int) -> type_identity; + template + auto try_add_rvalue_reference(...) -> type_identity; + } // namespace detail + + template + struct add_lvalue_reference + : decltype(detail::try_add_lvalue_reference(0)) {}; + + template + struct add_rvalue_reference + : decltype(detail::try_add_rvalue_reference(0)) {}; + + template + typename std::add_rvalue_reference::type declval() noexcept + { + static_assert(false, "declval not allowed in an evaluated context"); + } + + using nullptr_t = decltype(nullptr); + template + struct conditional { using type = T; }; + + template + struct conditional { using type = F; }; + + template struct remove_cv { typedef T type; }; + template struct remove_cv { typedef T type; }; + template struct remove_cv { typedef T type; }; + template struct remove_cv { typedef T type; }; + + template struct remove_const { typedef T type; }; + template struct remove_const { typedef T type; }; + + template struct remove_volatile { typedef T type; }; + template struct remove_volatile { typedef T type; }; + + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; + + template + struct is_floating_point + : std::integral_constant< + bool, + std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + > {}; + + template + struct is_integral + : std::integral_constant< + bool, + std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + > {}; + + template + struct is_arithmetic : std::integral_constant::value || + std::is_floating_point::value> {}; + + template + struct is_void : std::is_same::type> {}; + + namespace detail + { + template + auto test_returnable(int) -> decltype( + void(static_cast(nullptr)), std::true_type{} + ); + template + auto test_returnable(...) -> std::false_type; + + template + auto test_implicitly_convertible(int) -> decltype( + void(std::declval()(std::declval())), std::true_type{} + ); + template + auto test_implicitly_convertible(...) -> std::false_type; + } // namespace detail + + template + struct is_convertible : std::integral_constant(0))::value && + decltype(detail::test_implicitly_convertible(0))::value) || + (std::is_void::value && std::is_void::value) + > {}; +} \ No newline at end of file diff --git a/build/assets/lib/stdlib.c b/build/assets/lib/stdlib.c index c6c4363..2c47ff1 100644 --- a/build/assets/lib/stdlib.c +++ b/build/assets/lib/stdlib.c @@ -153,23 +153,6 @@ void* calloc(size_t n, size_t size) { return ret; } -void* memset(void* d, uint8_t c, size_t n) { - uint8_t* p = (uint8_t*)d; - while (n--) { - *p++ = c; - } - return d; -} - -void* memcpy(void* dest, const void* src, size_t n) { - uint8_t* d = (uint8_t*)dest; - const uint8_t* s = (const uint8_t*)src; - while (n--) { - *d++ = *s++; - } - return dest; -} - int memcmp(const void* s1, const void* s2, size_t n) { const uint8_t* p1 = (const uint8_t*)s1; const uint8_t* p2 = (const uint8_t*)s2; diff --git a/build/wasmPlugin.ts b/build/wasmPlugin.ts index 068668c..1c24ff2 100644 --- a/build/wasmPlugin.ts +++ b/build/wasmPlugin.ts @@ -65,11 +65,26 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin data = new DataView(memory.buffer); } }, - Math_sin(x) { return Math.sin(x); }, + Math_round(x) { return Math.round(x); }, + Math_floor(x) { return Math.floor(x); }, + Math_ceil(x) { return Math.ceil(x); }, + Math_exp(x) { return Math.exp(x); }, Math_log(x) { return Math.log(x); }, + Math_log10(x) { return Math.log10(x); }, Math_sqrt(x) { return Math.sqrt(x); }, - Math_pow(x,y) { return Math.pow(x, y); }, - Math_fmod(x,y) { return x % y; }, + Math_sin(x) { return Math.sin(x); }, + Math_cos(x) { return Math.cos(x); }, + Math_tan(x) { return Math.tan(x); }, + Math_asin(x) { return Math.asin(x); }, + Math_acos(x) { return Math.acos(x); }, + Math_atan(x) { return Math.atan(x); }, + Math_sinh(x) { return Math.sinh(x); }, + Math_cosh(x) { return Math.cosh(x); }, + Math_tanh(x) { return Math.tanh(x); }, + + Math_fmod(x, y) { return x % y; }, + Math_pow(x, y) { return Math.pow(x, y); }, + Math_atan2(x, y) { return Math.atan2(x, y); }, } }); @@ -91,16 +106,48 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin const include = `${buildAssets}/include`; const glob = new Bun.Glob(`${buildAssets}/lib/**/*.c`); const stdlib = await Array.fromAsync(glob.scan()); - const opt = production ? '-O3' : '-O0'; const objPath = wasmPath + '.o'; + + const features = [ + 'bulk-memory', + 'extended-const', + 'relaxed-simd', + 'simd128', + 'tail-call', + 'sign-ext', + 'nontrapping-fptoint', + 'reference-types', + 'multivalue', + + ].map(f => `-m${f}`); + + const flags = [ + '--target=wasm32', + production ? '-O3' : '-O0', + '-flto', + '-fno-builtin', + '--no-standard-libraries', + '-Wall', + '-Wextra', + '-Wpedantic', + '-Werror', + '-Wshadow', + ...features, + ]; const std = args.path.endsWith('.cpp') ? '-std=gnu++23': '-std=gnu23'; - const compileResult = await $`clang -c --target=wasm32 ${opt} -flto -fno-builtin --no-standard-libraries -I ${include} -Wall -Wextra -Wpedantic -Werror ${std} -o ${objPath} ${args.path}`; + const compileResult = await $`clang -c ${flags} ${std} -I ${include} -o ${objPath} ${args.path}`; if (compileResult.exitCode !== 0) { throw new Error('Compile failed, check output'); } - const linkResult = await $`clang --target=wasm32 ${opt} -flto -fno-builtin --no-standard-libraries -I ${include} -Wall -Wextra -Wpedantic -Werror -std=gnu23 -Wl,--lto-O3 -Wl,--no-entry -Wl,--import-memory -o ${wasmPath} ${objPath} ${stdlib}`; + const linkFlags = [ + '--lto-O3', + '--no-entry', + '--import-memory', + ].map(f => `-Wl,${f}`); + + const linkResult = await $`clang ${flags} -std=gnu23 -I ${include} ${linkFlags} -o ${wasmPath} ${objPath} ${stdlib}`; if (linkResult.exitCode !== 0) { throw new Error('Link failed, check output'); diff --git a/src/games/playground/awoo.cpp b/src/games/playground/awoo.cpp index b392391..1bbcb4c 100644 --- a/src/games/playground/awoo.cpp +++ b/src/games/playground/awoo.cpp @@ -1,15 +1,13 @@ -#include +#include -typedef struct { - union { - int a; - char b; - }; - const char* string; -} awoorwa; +using namespace linalg::aliases; -EXPORT(main) void init() { - awoorwa result = {.a = 42, .string = "awoo"}; +int awoo(auto arg) { + return arg() + 69; +} - printf("%d\n", result); +EXPORT(main) auto init(int arg) { + auto fn = [arg] () { return arg; }; + + return awoo(fn) + arg; } \ No newline at end of file