#ifndef VEC4_HPP #define VEC4_HPP #include "vec3.hpp" #include #include #include #include #include template class Vec4 { public: union { struct { T x, y, z, w; }; struct { T r, g, b, a; }; struct { T s, t, p, q; }; // For texture coordinates }; // Constructors Vec4() : x(0), y(0), z(0), w(0) {} Vec4(T x, T y, T z, T w) : x(x), y(y), z(z), w(w) {} Vec4(T scalar) : x(scalar), y(scalar), z(scalar), w(scalar) {} Vec4(const Vec3& rgb, T w = 1) : x(rgb.x), y(rgb.y), z(rgb.z), w(w) {} static Vec4 RGB(T r, T g, T b, T a = 1) { return Vec4(r, g, b, a); } static Vec4 RGBA(T r, T g, T b, T a) { return Vec4(r, g, b, a); } Vec4& recolor(const Vec4& newColor) { r = newColor.r; g = newColor.g; b = newColor.b; a = newColor.a; return *this; } Vec4 average(const Vec4& other) const { return Vec4((x + other.x) / 2, (y + other.y) / 2, (z + other.z) / 2, (w + other.w) / 2); } Vec4 operator+(const Vec4& other) const { return Vec4(x + other.x, y + other.y, z + other.z, w + other.w); } Vec4 operator-(const Vec4& other) const { return Vec4(x - other.x, y - other.y, z - other.z, w - other.w); } Vec4 operator*(const Vec4& other) const { return Vec4(x * other.x, y * other.y, z * other.z, w * other.w); } Vec4 operator/(const Vec4& other) const { return Vec4(x / other.x, y / other.y, z / other.z, w / other.w); } Vec4 operator+(T scalar) const { return Vec4(x + scalar, y + scalar, z + scalar, w + scalar); } Vec4 operator-(T scalar) const { return Vec4(x - scalar, y - scalar, z - scalar, w - scalar); } Vec4 operator-() const { return Vec4(-x, -y, -z, -w); } Vec4 operator*(T scalar) const { return Vec4(x * scalar, y * scalar, z * scalar, w * scalar); } Vec4 operator/(T scalar) const { return Vec4(x / scalar, y / scalar, z / scalar, w / scalar); } Vec4& operator=(T scalar) { x = y = z = w = scalar; return *this; } Vec4& operator+=(const Vec4& other) { x += other.x; y += other.y; z += other.z; w += other.w; return *this; } Vec4& operator-=(const Vec4& other) { x -= other.x; y -= other.y; z -= other.z; w -= other.w; return *this; } Vec4& operator*=(const Vec4& other) { x *= other.x; y *= other.y; z *= other.z; w *= other.w; return *this; } Vec4& operator/=(const Vec4& other) { x /= other.x; y /= other.y; z /= other.z; w /= other.w; return *this; } Vec4& operator+=(T scalar) { x += scalar; y += scalar; z += scalar; w += scalar; return *this; } Vec4& operator-=(T scalar) { x -= scalar; y -= scalar; z -= scalar; w -= scalar; return *this; } Vec4& operator*=(T scalar) { x *= scalar; y *= scalar; z *= scalar; w *= scalar; return *this; } Vec4& operator/=(T scalar) { x /= scalar; y /= scalar; z /= scalar; w /= scalar; return *this; } T dot(const Vec4& other) const { return x * other.x + y * other.y + z * other.z + w * other.w; } // 4D cross product (returns vector perpendicular to 3 given vectors in 4D space) Vec4 cross(const Vec4& v1, const Vec4& v2, const Vec4& v3) const { T a = v1.y * (v2.z * v3.w - v2.w * v3.z) - v1.z * (v2.y * v3.w - v2.w * v3.y) + v1.w * (v2.y * v3.z - v2.z * v3.y); T b = -v1.x * (v2.z * v3.w - v2.w * v3.z) + v1.z * (v2.x * v3.w - v2.w * v3.x) - v1.w * (v2.x * v3.z - v2.z * v3.x); T c = v1.x * (v2.y * v3.w - v2.w * v3.y) - v1.y * (v2.x * v3.w - v2.w * v3.x) + v1.w * (v2.x * v3.y - v2.y * v3.x); T d = -v1.x * (v2.y * v3.z - v2.z * v3.y) + v1.y * (v2.x * v3.z - v2.z * v3.x) - v1.z * (v2.x * v3.y - v2.y * v3.x); return Vec4(a, b, c, d); } T length() const { return static_cast(std::sqrt(static_cast(x * x + y * y + z * z + w * w))); } T lengthSquared() const { return x * x + y * y + z * z + w * w; } T distance(const Vec4& other) const { return (*this - other).length(); } T distanceSquared(const Vec4& other) const { Vec4 diff = *this - other; return diff.x * diff.x + diff.y * diff.y + diff.z * diff.z + diff.w * diff.w; } Vec4 normalized() const { T len = length(); if (len > 0) { return *this / len; } return *this; } // Homogeneous normalization (divide by w) Vec4 homogenized() const { if (w != 0) { return Vec4(x / w, y / w, z / w, 1); } return *this; } // Clamp values between 0 and 1 Vec4 clamped() const { return Vec4( std::clamp(r, static_cast(0), static_cast(1)), std::clamp(g, static_cast(0), static_cast(1)), std::clamp(b, static_cast(0), static_cast(1)), std::clamp(a, static_cast(0), static_cast(1)) ); } // Convert to Vec3 (ignoring alpha) Vec3 toVec3() const { return Vec3(r, g, b); } // Convert to 8-bit color values template typename std::enable_if::value>::type toUint8(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha) const { red = static_cast(std::clamp(r, static_cast(0), static_cast(1)) * 255); green = static_cast(std::clamp(g, static_cast(0), static_cast(1)) * 255); blue = static_cast(std::clamp(b, static_cast(0), static_cast(1)) * 255); alpha = static_cast(std::clamp(a, static_cast(0), static_cast(1)) * 255); } template typename std::enable_if::value>::type toUint8(uint8_t& red, uint8_t& green, uint8_t& blue) const { red = static_cast(std::clamp(r, static_cast(0), static_cast(1)) * 255); green = static_cast(std::clamp(g, static_cast(0), static_cast(1)) * 255); blue = static_cast(std::clamp(b, static_cast(0), static_cast(1)) * 255); } // Get XYZ components as Vec3 Vec3 xyz() const { return Vec3(x, y, z); } // Get RGB components as Vec3 Vec3 rgb() const { return Vec3(r, g, b); } bool operator==(const Vec4& other) const { return x == other.x && y == other.y && z == other.z && w == other.w; } bool operator!=(const Vec4& other) const { return x != other.x || y != other.y || z != other.z || w != other.w; } bool operator<(const Vec4& other) const { return (x < other.x) || (x == other.x && y < other.y) || (x == other.x && y == other.y && z < other.z) || (x == other.x && y == other.y && z == other.z && w < other.w); } bool operator<=(const Vec4& other) const { return (x < other.x) || (x == other.x && y < other.y) || (x == other.x && y == other.y && z < other.z) || (x == other.x && y == other.y && z == other.z && w <= other.w); } bool operator>(const Vec4& other) const { return (x > other.x) || (x == other.x && y > other.y) || (x == other.x && y == other.y && z > other.z) || (x == other.x && y == other.y && z == other.z && w > other.w); } bool operator>=(const Vec4& other) const { return (x > other.x) || (x == other.x && y > other.y) || (x == other.x && y == other.y && z > other.z) || (x == other.x && y == other.y && z == other.z && w >= other.w); } Vec4 abs() const { return Vec4(std::abs(x), std::abs(y), std::abs(z), std::abs(w)); } Vec4 floor() const { return Vec4(std::floor(x), std::floor(y), std::floor(z), std::floor(w)); } Vec4 ceil() const { return Vec4(std::ceil(x), std::ceil(y), std::ceil(z), std::ceil(w)); } Vec4 round() const { return Vec4(std::round(x), std::round(y), std::round(z), std::round(w)); } Vec4 min(const Vec4& other) const { return Vec4(std::min(x, other.x), std::min(y, other.y), std::min(z, other.z), std::min(w, other.w)); } Vec4 max(const Vec4& other) const { return Vec4(std::max(x, other.x), std::max(y, other.y), std::max(z, other.z), std::max(w, other.w)); } Vec4 clamp(T minVal, T maxVal) const { return Vec4( std::clamp(x, minVal, maxVal), std::clamp(y, minVal, maxVal), std::clamp(z, minVal, maxVal), std::clamp(w, minVal, maxVal) ); } // Color-specific clamping (clamps RGB between 0 and 1) Vec4 clampColor() const { return Vec4( std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f), std::clamp(a, 0.0f, 1.0f) ); } bool isZero(T epsilon = static_cast(1e-10)) const { return std::abs(x) < epsilon && std::abs(y) < epsilon && std::abs(z) < epsilon && std::abs(w) < epsilon; } bool equals(const Vec4& other, T epsilon = static_cast(1e-10)) const { return std::abs(x - other.x) < epsilon && std::abs(y - other.y) < epsilon && std::abs(z - other.z) < epsilon && std::abs(w - other.w) < epsilon; } friend Vec4 operator+(T scalar, const Vec4& vec) { return Vec4(scalar + vec.x, scalar + vec.y, scalar + vec.z, scalar + vec.w); } friend Vec4 operator-(T scalar, const Vec4& vec) { return Vec4(scalar - vec.x, scalar - vec.y, scalar - vec.z, scalar - vec.w); } friend Vec4 operator*(T scalar, const Vec4& vec) { return Vec4(scalar * vec.x, scalar * vec.y, scalar * vec.z, scalar * vec.w); } friend Vec4 operator/(T scalar, const Vec4& vec) { return Vec4(scalar / vec.x, scalar / vec.y, scalar / vec.z, scalar / vec.w); } Vec4 lerp(const Vec4& other, T t) const { t = std::clamp(t, static_cast(0), static_cast(1)); return *this + (other - *this) * t; } // Convert to grayscale using standard RGB weights (only valid for float/double) template typename std::enable_if::value, T>::type grayscale() const { return r * static_cast(0.299) + g * static_cast(0.587) + b * static_cast(0.114); } // Color inversion (1.0 - color) (only valid for float/double) template typename std::enable_if::value, Vec4>::type inverted() const { return Vec4(static_cast(1) - r, static_cast(1) - g, static_cast(1) - b, a); } T& operator[](int index) { return (&x)[index]; } const T& operator[](int index) const { return (&x)[index]; } std::string toString() const { return "(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ", " + std::to_string(w) + ")"; } template typename std::enable_if::value, std::string>::type toColorString() const { return "RGBA(" + std::to_string(r) + ", " + std::to_string(g) + ", " + std::to_string(b) + ", " + std::to_string(a) + ")"; } struct Hash { std::size_t operator()(const Vec4& v) const { return std::hash()(v.x) ^ (std::hash()(v.y) << 1) ^ (std::hash()(v.z) << 2) ^ (std::hash()(v.w) << 3); } }; }; // Type aliases for common use cases using Vec4f = Vec4; using Vec4d = Vec4; using Vec4i = Vec4; using Vec4u = Vec4; using Vec4ui8 = Vec4; template inline std::ostream& operator<<(std::ostream& os, const Vec4& vec) { os << vec.toString(); return os; } namespace std { template struct hash> { size_t operator()(const Vec4& v) const { return hash()(v.x) ^ (hash()(v.y) << 1) ^ (hash()(v.z) << 2) ^ (hash()(v.w) << 3); } }; } #endif