I've created this 3D vector class and would like it to be reviewed. I wonder if there are still some improvements that can be made. This is a header only implementation.
#pragma once
#include <string>
template<class T> class vec3
{
public:
union {
T f[3];
struct {
T x, y, z;
};
};
vec3(vec2<T> p, T z = 0) :
x(p.x),
y(p.y),
z(z)
{
}
vec3(T x = 0, T y = 0, T z = 0) :
x(x),
y(y),
z(z)
{
}
template <typename _L> operator vec2<_L>() const { return vec3<_L>(x, y); };
template <typename _L> operator vec3<_L>() const { return vec3<_L>(x, y, z); };
vec3 operator-() const {
return vec3<T>(-x, -y, -z);
};
bool operator==(const vec3& other) const {
return (x == other.x && y == other.y && z == other.z);
};
bool operator!=(const vec3& other) const {
return !operator==(other);
};
bool operator<(const vec3& other) const {
if (x < other.x)
return true;
if (x > other.x)
return false;
//x == other.x
if (y < other.y)
return true;
if (y > other.y)
return false;
//y == other.y
if (z < other.z)
return true;
if (z > other.z)
return false;
//z == other.z
return false;
};
bool operator>(const vec3& other) const {
return other < *this;
};
bool operator<=(const vec3& other) const {
return !operator>(other);
};
bool operator>=(const vec3& other) const {
return !operator<(other);
};
vec3& operator*= (const vec3& other) {
x *= other.x;
y *= other.y;
z *= other.z;
return *this;
};
vec3 operator* (const vec3& other) const {
return vec3<T>(x * other.x, y * other.y, z * other.z);
};
vec3& operator/= (const vec3& other) {
x /= other.x;
y /= other.y;
z /= other.z;
return *this;
};
vec3 operator/ (const vec3& other) const {
return vec3<T>(x / other.x, y / other.y, z / other.z);
};
vec3& operator+= (const vec3& other) {
x += other.x;
y += other.y;
z += other.z;
return *this;
};
vec3 operator+ (const vec3& other) const {
return vec3<T>(x + other.x, y + other.y, z + other.z);
};
vec3& operator-= (const vec3& other) {
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
};
vec3 operator- (const vec3& other) const {
return vec3<T>(x - other.x, y - other.y, z - other.z);
};
vec3& operator*= (const T f) {
x *= f;
y *= f;
z *= f;
return *this;
};
vec3 operator* (const T f) const {
return vec3<T>(x*f, y*f, z*f);
};
vec3& operator/= (const T f) {
x /= f;
y /= f;
z /= f;
return *this;
};
vec3 operator/ (const T f) const {
return vec3<T>(x / f, y / f, z / f);
};
vec3& operator+= (const T f) {
x += f;
y += f;
z += f;
return *this;
};
vec3 operator+ (const T f) const {
return vec3<T>(x + f, y + f, z + f);
};
vec3& operator-= (const T f) {
x -= f;
y -= f;
z -= f;
return *this;
};
vec3 operator- (const T f) const {
return vec3<T>(x - f, y - f, z - f);
};
/* Returns the magnitude of this vector. */
T magnitude() const {
return sqrt(sqrMagnitude());
};
/* Returns the squared magnitude of this vector. */
T sqrMagnitude() const {
return x*x + y*y + z*z;
};
/* Returns the inverse of this vector (1 / vector). */
vec3& invert() {
return ((*this) = 1 / (*this));
}
/* Returns a copy of this vector inverted (1 / vector). */
vec3 inverse() const {
return (1 / (*this));
}
/* Normalizes this vector. */
vec3& normalize() {
return ((*this) *= 1 / magnitude());
};
/* Returns a copy of this vector normalized. */
vec3 normalized() const {
return ((*this) * 1 / magnitude());
};
/* Return the dot product of this vector and 'other'. */
T dot(const vec3& other) const {
return x*other.x + y*other.y + z*other.z;
};
/* Return the dot product of vector a and b. */
static T dot(const vec3& a, const vec3& b) {
return a.x*b.x + a.y*b.y + a.z*b.z;
};
/* Return the cross product of this vector and 'other'. */
vec3 cross(const vec3& other) const {
return vec3<T>(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x);
};
/* Return the cross product of vector a and b. */
static vec3 cross(const vec3& a, const vec3& b) {
return vec3<T>(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
};
/* Returns the distance from this vector to 'to'. */
T distance(const vec3& to) const {
return (*this - to).magnitude();
};
/* Returns the distance between vector a and b. */
static T distance(const vec3& a, const vec3& b) {
return (a - b).magnitude();
};
/* Returns a linearly interpolated vector between this vector and 'other' based on t. */
vec3 lerp(const vec3& other, const float t) const {
return ((1.0f - t)*(*this) + t*other);
};
/* Returns a linearly interpolated vector between vector a and b based on t. */
static vec3 lerp(const vec3& a, const vec3& b, const float t) {
return ((1.0f - t)*a + t*b);
};
/* Returns a spherically interpolated vector between this vector and 'other' based on t. */
vec3 slerp(const vec3& other, float t) const {
float dotproduct = dot(other);
t /= 2;
float theta = acos(dotproduct);
if (theta < 0.0f)
theta = -theta;
float st = sin(theta);
float coeff1 = sin((1 - t)*theta) / st;
float coeff2 = sin(t*theta) / st;
return (coeff1*(*this) + coeff2*other).normalize();
};
/* Returns a spherically interpolated vector between vector a and b based on t. */
static vec3 slerp(const vec3& a, const vec3& b, float t) {
float dotproduct = dot(a, b);
t /= 2;
float theta = acos(dotproduct);
if (theta < 0.0f)
theta = -theta;
float st = sin(theta);
float coeff1 = sin((1 - t)*theta) / st;
float coeff2 = sin(t*theta) / st;
return (coeff1*a + coeff2*b).normalize();
};
/* Calculate the angle between two vectors in radians. */
T angle(const vec3& to) {
float dot = (*this).dot(to);
dot /= ((*this).magnitude() * to.magnitude());
return acos(dot);
};
/* Calculate the angle between two vectors in radians. */
static T angle(const vec3& from, const vec3& to) {
float dot = from.dot(to);
dot /= (from.magnitude() * to.magnitude());
return acos(dot);
};
/* Returns a structured string representation of this vector. */
std::string toString() const {
return "[x: " + std::to_string(x) + ", y: " + std::to_string(y) + ", z: " + std::to_string(z) + "]";
};
friend vec3<T> operator* (const T d, const vec3& vec) {
return vec3<T>(d * vec.x, d * vec.y, d * vec.z);
};
friend vec3<T> operator/ (const T d, const vec3& vec) {
return vec3<T>(d / vec.x, d / vec.y, d / vec.z);
};
friend vec3<T> operator+ (const T d, const vec3& vec) {
return vec3<T>(d + vec.x, d + vec.y, d + vec.z);
};
friend vec3<T> operator- (const T d, const vec3& vec) {
return vec3<T>(d - vec.x, d - vec.y, d - vec.z);
};
static const vec3 left;
static const vec3 right;
static const vec3 up;
static const vec3 down;
static const vec3 forward;
static const vec3 back;
static const vec3 zero;
static const vec3 one;
};
template <typename T> const vec3<T> vec3<T>::left(-1, 0, 0);
template <typename T> const vec3<T> vec3<T>::right(1, 0, 0);
template <typename T> const vec3<T> vec3<T>::up(0, 1, 0);
template <typename T> const vec3<T> vec3<T>::down(0, -1, 0);
template <typename T> const vec3<T> vec3<T>::forward(0, 0, 1);
template <typename T> const vec3<T> vec3<T>::back(0, 0, -1);
template <typename T> const vec3<T> vec3<T>::zero(0, 0, 0);
template <typename T> const vec3<T> vec3<T>::one(1, 1, 1);