//SPDX-License-Identifier: LGPL-3.0-only //SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® export module Crafter.Math:Vector; import std; namespace Crafter { template struct __attribute__((packed)) VectorBase { T v[Aligment]; }; template struct __attribute__((packed)) VectorBase { union { T v[Aligment]; T x, r; }; }; template struct __attribute__((packed)) VectorBase { union { T v[Aligment]; struct __attribute__((packed)) { T x, y; }; struct __attribute__((packed)) { T r, g; }; }; constexpr VectorBase(T x, T y): x(x), y(y) { } constexpr VectorBase() = default; }; template struct __attribute__((packed)) VectorBase { union { T v[Aligment]; struct __attribute__((packed)) { T x, y, z; }; struct __attribute__((packed)) { T r, g, b; }; }; constexpr VectorBase(T x, T y, T z): x(x), y(y), z(z) { } constexpr VectorBase() = default; }; template struct __attribute__((packed)) VectorBase { union { T v[Aligment]; struct __attribute__((packed)) { T x, y, z, w; }; struct __attribute__((packed)) { T r, g, b, a; }; }; constexpr VectorBase(T x, T y, T z, T w): x(x), y(y), z(z), w(w) { } constexpr VectorBase() = default; }; template struct VectorBase { T v[Len]; }; template struct VectorBase { union { T v[1]; T x, r; }; }; template struct VectorBase { union { T v[2]; struct { T x, y; }; struct { T r, g; }; }; constexpr VectorBase(T x, T y): x(x), y(y) { } constexpr VectorBase() = default; }; template struct VectorBase { union { T v[3]; struct { T x, y, z; }; struct { T r, g, b; }; }; constexpr VectorBase(T x, T y, T z): x(x), y(y), z(z) { } constexpr VectorBase() = default; }; template struct VectorBase { union { T v[4]; struct { T x, y, z, w; }; struct { T r, g, b, a; }; }; constexpr VectorBase(T x, T y, T z, T w): x(x), y(y), z(z), w(w) { } constexpr VectorBase() = default; }; export template class Vector : public VectorBase { public: constexpr Vector(T x, T y, T z, T w ) requires(std::same_as && Len == 4) : VectorBase(x, y, z, w) { } constexpr Vector(T x, T y, T z) requires(std::same_as && Len == 3) : VectorBase(x, y, z) { } constexpr Vector(T x, T y) requires(std::same_as && Len == 2) : VectorBase(x, y) { } constexpr Vector() = default; template constexpr operator Vector() const { Vector returnVector; for(std::uint32_t i = 0; i < Len; i++) { returnVector.v[i] = this->v[i]; } return returnVector; } template constexpr Vector operator+(Vector b) const { Vector resultVector; for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { resultVector.v[i] = this->v[i]+b.v[i]; } return resultVector; } template constexpr Vector operator-(Vector b) const { Vector resultVector; for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { resultVector.v[i] = this->v[i]-b.v[i]; } return resultVector; } constexpr Vector operator-(){ Vector resultVector; for(std::uint32_t i = 0; i < Len; i++) { resultVector.v[i] = -this->v[i]; } return resultVector; } template constexpr Vector operator*(Vector b) const { Vector resultVector; for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { resultVector.v[i] = this->v[i]*b.v[i]; } return resultVector; } template constexpr Vector operator/(Vector b) const { Vector resultVector; for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { resultVector.v[i] = this->v[i]/b.v[i]; } return resultVector; } template constexpr void operator+=(Vector b){ for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { this->v[i]+=b.v[i]; } } template constexpr void operator-=(Vector b){ for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { this->v[i]-=b.v[i]; } } template constexpr void operator*=(Vector b){ for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { this->v[i]*=b.v[i]; } } template constexpr void operator/=(Vector b){ for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { this->v[i]/=b.v[i]; } } template constexpr Vector operator+(BT b) const requires std::is_arithmetic_v { Vector resultVector; for(std::uint32_t i = 0; i < Len; i++) { resultVector.v[i] = this->v[i] + b; } return resultVector; } template constexpr Vector operator-(BT b) const requires std::is_arithmetic_v { Vector resultVector; for(std::uint32_t i = 0; i < Len; i++) { resultVector.v[i] = this->v[i] - b; } return resultVector; } template constexpr Vector operator*(BT b) const requires std::is_arithmetic_v { Vector resultVector; for(std::uint32_t i = 0; i < Len; i++) { resultVector.v[i] = this->v[i] * b; } return resultVector; } template constexpr Vector operator/(BT b) const requires std::is_arithmetic_v { Vector resultVector; for(std::uint32_t i = 0; i < Len; i++) { resultVector.v[i] = this->v[i] / b; } return resultVector; } template constexpr void operator+=(BT b) requires std::is_arithmetic_v{ for(std::uint32_t i = 0; i < Len; i++) { this->v[i] += b; } } template constexpr void operator-=(BT b) requires std::is_arithmetic_v{ for(std::uint32_t i = 0; i < Len; i++) { this->v[i] -= b; } } template constexpr void operator*=(BT b) requires std::is_arithmetic_v{ for(std::uint32_t i = 0; i < Len; i++) { this->v[i] *= b; } } template constexpr void operator/=(BT b) requires std::is_arithmetic_v{ for(std::uint32_t i = 0; i < Len; i++) { this->v[i] /= b; } } template constexpr bool operator==(Vector b) const { for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { if(this->v[i] != b.v[i]) { return false; } } return true; } template constexpr bool operator!=(Vector b) const { for(std::uint32_t i = 0; i < std::min(Len, Blen); i++) { if(this->v[i] != b.v[i]) { return true; } } return false; } constexpr void Normalize() { T fLength = Length(); fLength = 1.0f / fLength; for(std::uint32_t i = 0; i < Len; i++) { this->v[i] *= fLength; } } constexpr void Invert() { for(std::uint32_t i = 0; i < Len; i++) { this->v[i] = -this->v[i]; } } constexpr T Length() const { T Result = LengthSq(); return std::sqrtf(Result); } constexpr T LengthSq() const { return Dot(*this, *this); } template constexpr static Vector Cross(Vector a, Vector b) requires(Len == 3 && Alen >= 3 && Blen >= 3) { return Vector( (a.v[1] * b.v[2]) - (a.v[2] * b.v[1]), (a.v[2] * b.v[0]) - (a.v[0] * b.v[2]), (a.v[0] * b.v[1]) - (a.v[1] * b.v[0]) ); } template constexpr static Vector Normalize(Vector a) requires(Len == Alen) { Vector returned; T fLength = a.Length(); if (fLength > 0) { fLength = 1.0f / fLength; } for(std::uint32_t i = 0; i < Len; i++) { returned.v[i] = a.v[i] * fLength; } return returned; } template constexpr static T Dot(Vector a, Vector b) requires(Alen >= Len && Blen >= Len) { T accumulate = a.v[0] * b.v[0]; for(std::uint32_t i = 1; i < Len; i++) { accumulate += a.v[i] * b.v[i]; } return accumulate; } template constexpr static Vector Rotate(Vector v, Vector q) requires(Len == 3) { Vector qv(q.x, q.y, q.z); Vector t = Vector::Cross(qv, v) * T(2); return v + t * q.w + Vector::Cross(qv, t); } template constexpr static Vector RotatePivot(Vector v, Vector q, Vector pivot) requires(Len == 3) { Vector translated = v - pivot; Vector qv(q.x, q.y, q.z); Vector t = Cross(qv, translated) * T(2); Vector rotated = translated + t * q.w +Cross(qv, t); return rotated + pivot; } template constexpr static Vector QuanternionFromBasis(Vector right, Vector up, Vector forward) requires(Len == 4) { T m00 = right.x; T m01 = up.x; T m02 = forward.x; T m10 = right.y; T m11 = up.y; T m12 = forward.y; T m20 = right.z; T m21 = up.z; T m22 = forward.z; T trace = m00 + m11 + m22; Vector q; if (trace > std::numeric_limits::epsilon()) { T s = std::sqrt(trace + T(1)) * T(2); q.w = T(0.25) * s; q.x = (m21 - m12) / s; q.y = (m02 - m20) / s; q.z = (m10 - m01) / s; } else if ((m00 > m11) && (m00 > m22)) { T s = std::sqrt(T(1) + m00 - m11 - m22) * T(2); q.w = (m21 - m12) / s; q.x = T(0.25) * s; q.y = (m01 + m10) / s; q.z = (m02 + m20) / s; } else if (m11 > m22) { T s = std::sqrt(T(1) + m11 - m00 - m22) * T(2); q.w = (m02 - m20) / s; q.x = (m01 + m10) / s; q.y = T(0.25) * s; q.z = (m12 + m21) / s; } else { T s = std::sqrt(T(1) + m22 - m00 - m11) * T(2); q.w = (m10 - m01) / s; q.x = (m02 + m20) / s; q.y = (m12 + m21) / s; q.z = T(0.25) * s; } q.Normalize(); return q; } constexpr static Vector QuanternionFromEuler(T rollHalf, T pitchHalf, T yawHalf) { T cr = std::cos(rollHalf); T sr = std::sin(rollHalf); T cp = std::cos(pitchHalf); T sp = std::sin(pitchHalf); T cy = std::cos(yawHalf); T sy = std::sin(yawHalf); return Vector( sr * cp * cy - cr * sp * sy, cr * sp * cy + sr * cp * sy, cr * cp * sy - sr * sp * cy, cr * cp * cy + sr * sp * sy ); } // The quaternions here are Vector in (x, y, z, w) order, w the // scalar part, applied as Rotate does (q v q*). They take and return // unit quaternions. // The Hamilton product: rotation b followed by rotation a, so // Rotate(v, QuanternionMultiply(a, b)) == Rotate(Rotate(v, b), a). template constexpr static Vector QuanternionMultiply(Vector a, Vector b) requires(Len == 4) { return Vector( a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x, a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z ); } // The inverse rotation. template constexpr static Vector QuanternionConjugate(Vector q) requires(Len == 4) { return Vector(-q.x, -q.y, -q.z, q.w); } // `angle` radians about the unit vector `axis`, right-handed: a // positive angle about +Z turns +X towards +Y. template constexpr static Vector QuanternionFromAxisAngle(Vector axis, T angle) requires(Len == 4) { const T s = std::sin(angle * T(0.5)); return Vector(axis.x * s, axis.y * s, axis.z * s, std::cos(angle * T(0.5))); } // The shortest rotation taking unit direction `from` onto unit // direction `to`. For opposite directions every axis perpendicular // to `from` is a shortest arc, and the cross product that picks the // axis otherwise is zero; the axis is then taken against whichever of // X and Y is further from parallel to `from`. template constexpr static Vector QuanternionFromUnitVectors(Vector from, Vector to) requires(Len == 4) { const Vector f(from.x, from.y, from.z); const T d = f.x * to.x + f.y * to.y + f.z * to.z; if (d < T(-1) + T(1e-6)) { Vector axis = std::abs(f.x) < T(0.9) ? Vector::Cross(Vector(1, 0, 0), f) : Vector::Cross(Vector(0, 1, 0), f); axis.Normalize(); return Vector(axis.x, axis.y, axis.z, T(0)); } const Vector c = Vector::Cross(f, Vector(to.x, to.y, to.z)); Vector q(c.x, c.y, c.z, T(1) + d); q.Normalize(); return q; } // Normalized linear interpolation from a to b, along the shorter arc // (q and -q are the same rotation, and blending towards the wrong one // swings the long way round). Cheaper than Slerp; its angular speed // varies a little along the arc, which damping and blending between // nearby poses don't notice. template constexpr static Vector QuanternionNlerp(Vector a, Vector b, T t) requires(Len == 4) { const T sign = (a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w) < T(0) ? T(-1) : T(1); Vector q( a.x + (b.x * sign - a.x) * t, a.y + (b.y * sign - a.y) * t, a.z + (b.z * sign - a.z) * t, a.w + (b.w * sign - a.w) * t ); q.Normalize(); return q; } // Spherical linear interpolation from a to b: constant angular speed, // along the shorter arc. Nearly identical inputs go through Nlerp, // which is exact enough there, instead of dividing by sin(~0). template constexpr static Vector QuanternionSlerp(Vector a, Vector b, T t) requires(Len == 4) { T cosAngle = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; T sign = T(1); if (cosAngle < T(0)) { cosAngle = -cosAngle; sign = T(-1); } if (cosAngle > T(0.9995)) { return QuanternionNlerp(a, b, t); } const T angle = std::acos(cosAngle); const T sinAngle = std::sin(angle); const T wa = std::sin((T(1) - t) * angle) / sinAngle; const T wb = std::sin(t * angle) / sinAngle * sign; return Vector( a.x * wa + b.x * wb, a.y * wa + b.y * wb, a.z * wa + b.z * wb, a.w * wa + b.w * wb ); } }; } template struct std::formatter> : std::formatter { auto format(const Crafter::Vector& vec, format_context& ctx) const { std::string out = "{"; for(std::uint32_t i2 = 0; i2 < Len; i2++) { #ifdef __x86_64 if constexpr(std::same_as) { out += std::format("{}", static_cast(vec.v[i2])); } else #endif { out += std::format("{}", vec.v[i2]); } if (i2 + 1 < Len) out += ","; } out += "}"; return std::formatter::format(out, ctx); } }; template constexpr Crafter::Vector operator*(BT b, const Crafter::Vector& v) requires std::is_arithmetic_v { return v * b; }