- added my good old Vector class template implementation (we need it later for the...
authorAkiko <akiko@linux-addicted.net>
Wed, 16 Oct 2013 09:02:20 +0000 (11:02 +0200)
committerAkiko <akiko@linux-addicted.net>
Wed, 16 Oct 2013 09:02:20 +0000 (11:02 +0200)
engine/Vector.hxx [new file with mode: 0644]

diff --git a/engine/Vector.hxx b/engine/Vector.hxx
new file mode 100644 (file)
index 0000000..e9b2731
--- /dev/null
@@ -0,0 +1,1705 @@
+#pragma once
+
+/*
+ *  Using Qt here would be nuts. Qt's vector components are no templates and I need some more control of that very
+ *  important part of the engine code. So I made my own vector classes (also a bit more flexible) ...
+ *  - Vector2 for GUI and 2D stuff
+ *  - Vector3 for the common 3D stuff
+ *  - Vector4 for the matrix and quaternion magic
+ *  ~ Akiko ~
+ */
+
+#include <cmath>
+#include <iostream>
+#include <sstream>
+#include <type_traits>
+
+template <typename MT> class Vector2;
+template <typename MT> class Vector3;
+template <typename MT> class Vector4;
+
+// ****************************************************************************************************************** //
+
+template <typename MT>
+class Vector2 {
+    static_assert (std::is_integral<MT>::value || std::is_floating_point<MT>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+public:
+    // --- data structures ---
+
+    union {
+        struct {
+            MT X;
+            MT Y;
+        };
+        MT XY[2];
+    } __attribute__((packed));
+
+    // --- constructors and deconstructors ---
+
+    Vector2()
+    : XY{0, 0}
+    {
+    }
+
+    Vector2(const MT& val)
+    : XY{val, val}
+    {
+    }
+
+    Vector2(const MT& x, const MT& y)
+    : XY{x, y}
+    {
+    }
+
+    Vector2(const Vector3<MT>& vec)
+    : XY{vec.X, vec.Y}
+    {
+    }
+
+    Vector2(const Vector4<MT>& vec)
+    : XY{vec.X, vec.Y}
+    {
+    }
+
+    Vector2(const Vector2& rhs)
+    : XY{rhs.X, rhs.Y}
+    {
+    }
+
+    Vector2(const Vector2&& rhs)
+    : XY{std::move(rhs.X), std::move(rhs.Y)}
+    {
+    }
+
+    virtual ~Vector2()
+    {
+    }
+
+    // --- default operators ---
+
+    Vector2& operator=(const Vector2& rhs)
+    {
+        if (this != &rhs)
+        {
+            X = rhs.X;
+            Y = rhs.Y;
+        }
+
+        return *this;
+    }
+
+    Vector2& operator=(const Vector2&& rhs)
+    {
+        if (this != &rhs)
+        {
+            X = std::move(rhs.X);
+            Y = std::move(rhs.Y);
+        }
+
+        return *this;
+    }
+
+    bool operator==(const Vector2& rhs)
+    {
+        return X == rhs.X && Y == rhs.Y;
+    }
+
+    bool operator!=(const Vector2& rhs)
+    {
+        return !(*this == rhs);
+    }
+
+    const MT operator[](const size_t& index) const
+    {
+        return XY[index];
+    }
+
+    MT& operator[](const size_t& index)
+    {
+        return XY[index];
+    }
+
+    Vector2& operator~()
+    {
+        X = -X;
+        Y = -Y;
+
+        return *this;
+    }
+
+    Vector2 operator-() const
+    {
+        return {-X, -Y};
+    }
+
+    // --- other operators ---
+
+    Vector2& operator+=(const Vector2& rhs)
+    {
+        X += rhs.X;
+        Y += rhs.Y;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector2& operator+=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X += val;
+        Y += val;
+
+        return *this;
+    }
+
+    Vector2 operator+(const Vector2& rhs) const
+    {
+        return {X + rhs.X, Y + rhs.Y};
+    }
+
+    template <typename T>
+    Vector2 operator+(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X + val, Y + val};
+    }
+
+    // ---
+
+    Vector2& operator-=(const Vector2& rhs)
+    {
+        X -= rhs.X;
+        Y -= rhs.Y;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector2& operator-=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X -= val;
+        Y -= val;
+
+        return *this;
+    }
+
+    Vector2 operator-(const Vector2& rhs) const
+    {
+        return {X - rhs.X, Y - rhs.Y};
+    }
+
+    template <typename T>
+    Vector2 operator-(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X - val, Y - val};
+    }
+
+    // ---
+
+    Vector2& operator*=(const Vector2& rhs)
+    {
+        X *= rhs.X;
+        Y *= rhs.Y;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector2& operator*=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X *= val;
+        Y *= val;
+
+        return *this;
+    }
+
+    Vector2 operator*(const Vector2& rhs) const
+    {
+        return {X * rhs.X, Y * rhs.Y};
+    }
+
+    template <typename T>
+    Vector2 operator*(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X * val, Y * val};
+    }
+
+    // ---
+
+    Vector2& operator/=(const Vector2& rhs)
+    {
+        X /= rhs.X;
+        Y /= rhs.Y;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector2& operator/=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X /= val;
+        Y /= val;
+
+        return *this;
+    }
+
+    Vector2 operator/(const Vector2& rhs) const
+    {
+        return {X / rhs.X, Y / rhs.Y};
+    }
+
+    template <typename T>
+    Vector2 operator/(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X / val, Y / val};
+    }
+
+    // --- public methods ---
+
+    MT distanceToLine(const Vector2& origin, const Vector2& direction) const
+    {
+        if (direction.isNull())
+            return (*this - origin).length();
+
+        return (*this - (origin + dotProduct(*this - origin, direction) * direction)).length();
+    }
+
+    MT distanceToLinePoints(const Vector2& point1, const Vector2& point2) const
+    {
+        const Vector2 direction = point2 - point1;
+
+        if (direction.isNull())
+            return (*this - point1).length();
+
+        return (*this - (point1 + dotProduct(*this - point1, direction) * direction)).length();
+    }
+
+    MT distanceToPoint(const Vector2& point) const
+    {
+        return (*this - point).length();
+    }
+
+    MT dotProduct(const Vector2& vec) const
+    {
+        return X * vec.X + Y * vec.Y;
+    }
+
+    bool isNull() const
+    {
+        return X == 0 && Y == 0;
+    }
+
+    MT length() const
+    {
+        return std::sqrt(X * X + Y * Y);
+    }
+
+    MT lengthSquared() const
+    {
+        return X * X + Y * Y;
+    }
+
+    void normalize()
+    {
+        *this /= std::sqrt(X * X + Y * Y);
+    }
+
+    Vector2 normalized() const
+    {
+        const MT len = std::sqrt(X * X + Y * Y);
+
+        return {X / len, Y / len};
+    }
+
+    std::string string() const
+    {
+        std::stringstream result;
+
+        result << X << ", " << Y;
+
+        return result.str();
+    }
+
+    Vector3<MT> vector3() const
+    {
+        return {X, Y, 0};
+    }
+
+    Vector4<MT> vector4() const
+    {
+        return {X, Y, 0, 0};
+    }
+
+    // --- static methods ---
+
+    static MT toDotProduct(const Vector2& vec1, const Vector2& vec2)
+    {
+        return vec1.X * vec2.X + vec1.Y * vec2.Y;
+    }
+
+    static std::string toString(const Vector2& vec)
+    {
+        std::stringstream result;
+
+        result << vec.X << ", " << vec.Y;
+
+        return result.str();
+    }
+
+    static Vector3<MT> toVector3(const Vector2& vec)
+    {
+        return {vec.X, vec.Y, 0};
+    }
+
+    static Vector4<MT> toVector4(const Vector2& vec)
+    {
+        return {vec.X, vec.Y, 0, 0};
+    }
+};
+
+// --- global operators ---
+
+template <typename T>
+bool operator==(const Vector2<T>& lhs, const Vector2<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return lhs.X == rhs.X && lhs.Y == rhs.Y;
+}
+
+template <typename T>
+bool operator!=(const Vector2<T> lhs, const Vector2<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return !(lhs == rhs);
+}
+
+template <typename T>
+std::ostream& operator<<(std::ostream& lhs, const Vector2<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs << rhs.X << rhs.Y;
+
+    return lhs;
+}
+
+template <typename T>
+std::istream& operator>>(std::istream& lhs, Vector2<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs >> rhs.X >> rhs.Y;
+
+    return lhs;
+}
+
+// ---
+
+template <typename T>
+Vector2<T> operator+(const Vector2<T>& vec1, const Vector2<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X + vec2.X, vec1.Y + vec2.Y};
+}
+
+template <typename Vec, typename Val>
+Vector2<Vec> operator+(const Vector2<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X + val, vec.Y + val};
+}
+
+template <typename Val, typename Vec>
+Vector2<Vec> operator+(const Val& val, const Vector2<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val + vec.X, val + vec.Y};
+}
+
+// ---
+
+template <typename T>
+Vector2<T> operator-(const Vector2<T>& vec1, const Vector2<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X - vec2.X, vec1.Y - vec2.Y};
+}
+
+template <typename Vec, typename Val>
+Vector2<Vec> operator-(const Vector2<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X - val, vec.Y - val};
+}
+
+template <typename Val, typename Vec>
+Vector2<Vec> operator-(const Val& val, const Vector2<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val - vec.X, val - vec.Y};
+}
+
+template <typename T>
+Vector2<T> operator-(const Vector2<T>& vec)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {-vec.X, -vec.Y};
+}
+
+// ---
+
+template <typename T>
+Vector2<T> operator*(const Vector2<T>& vec1, const Vector2<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X * vec2.X, vec1.Y * vec2.Y};
+}
+
+template <typename Vec, typename Val>
+Vector2<Vec> operator*(const Vector2<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X * val, vec.Y * val};
+}
+
+template <typename Val, typename Vec>
+Vector2<Vec> operator*(const Val& val, const Vector2<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val * vec.X, val + vec.Y};
+}
+
+// ---
+
+template <typename T>
+Vector2<T> operator/(const Vector2<T>& vec1, const Vector2<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X / vec2.X, vec1.Y / vec2.Y};
+}
+
+template <typename Vec, typename Val>
+Vector2<Vec> operator/(const Vector2<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X / val, vec.Y / val};
+}
+
+template <typename Val, typename Vec>
+Vector2<Vec> operator/(const Val& val, const Vector2<Vec> vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val / vec.X, val / vec.Y};
+}
+
+// ****************************************************************************************************************** //
+
+template <typename MT>
+class Vector3 {
+    static_assert (std::is_integral<MT>::value || std::is_floating_point<MT>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+public:
+    // --- data structures ---
+
+    union {
+        struct {
+            MT X;
+            MT Y;
+            MT Z;
+        };
+        MT XYZ[3];
+    } __attribute__((packed));
+
+    // --- constructors and deconstructors ---
+
+    Vector3()
+    : XYZ{0, 0, 0}
+    {
+    }
+
+    Vector3(const MT& val)
+    : XYZ{val, val, val}
+    {
+    }
+
+    Vector3(const MT& x, const MT& y, const MT& z)
+    : XYZ{x, y, z}
+    {
+    }
+
+    Vector3(const Vector2<MT>& vec)
+    : XYZ{vec.X, vec.Y, 0}
+    {
+    }
+
+    Vector3(const Vector2<MT>& vec, const MT& val)
+    : XYZ{vec.X, vec.Y, val}
+    {
+    }
+
+    Vector3(const Vector4<MT>& vec)
+    : XYZ{vec.X, vec.Y, vec.Z}
+    {
+    }
+
+    Vector3(const Vector3& rhs)
+    : XYZ{rhs.X, rhs.Y, rhs.Z}
+    {
+    }
+
+    Vector3(const Vector3&& rhs)
+    : XYZ{std::move(rhs.X), std::move(rhs.Y), std::move(rhs.Z)}
+    {
+    }
+
+    virtual ~Vector3()
+    {
+    }
+
+    // --- default operators ---
+
+    Vector3& operator=(const Vector3& rhs)
+    {
+        if (this != &rhs)
+        {
+            X = rhs.X;
+            Y = rhs.Y;
+            Z = rhs.Z;
+        }
+
+        return *this;
+    }
+
+    Vector3& operator=(const Vector3&& rhs)
+    {
+        if (this != &rhs)
+        {
+            X = std::move(rhs.X);
+            Y = std::move(rhs.Y);
+            Z = std::move(rhs.Z);
+        }
+
+        return *this;
+    }
+
+    bool operator==(const Vector3& rhs) const
+    {
+        return X == rhs.X && Y == rhs.Y && Z == rhs.Z;
+    }
+
+    bool operator!=(const Vector3& rhs) const
+    {
+        return !(*this == rhs);
+    }
+
+    const MT operator[](const size_t& index) const
+    {
+        return XYZ[index];
+    }
+
+    MT& operator[](const size_t& index)
+    {
+        return XYZ[index];
+    }
+
+    Vector3& operator~()
+    {
+        X = -X;
+        Y = -Y;
+        Z = -Z;
+
+        return *this;
+    }
+
+    Vector3 operator-() const
+    {
+        return {-X, -Y, -Z};
+    }
+
+    // --- other operators ---
+
+    Vector3& operator+=(const Vector3& rhs)
+    {
+        X += rhs.X;
+        Y += rhs.Y;
+        Z += rhs.Z;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector3& operator+=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X += val;
+        Y += val;
+        Z += val;
+
+        return *this;
+    }
+
+    Vector3 operator+(const Vector3& rhs) const
+    {
+        return {X + rhs.X, Y + rhs.Y, Z + rhs.Z};
+    }
+
+    template <typename T>
+    Vector3 operator+(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X + val, Y + val, Z + val};
+    }
+
+    // ---
+
+    Vector3& operator-=(const Vector3& rhs)
+    {
+        X -= rhs.X;
+        Y -= rhs.Y;
+        Z -= rhs.Z;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector3& operator-=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X -= val;
+        Y -= val;
+        Z -= val;
+
+        return *this;
+    }
+
+    Vector3 operator-(const Vector3& rhs) const
+    {
+        return {X - rhs.X, Y - rhs.Y, Z - rhs.Z};
+    }
+
+    template <typename T>
+    Vector3 operator-(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X - val, Y - val, Z - val};
+    }
+
+    // ---
+
+    Vector3& operator*=(const Vector3& rhs)
+    {
+        X *= rhs.X;
+        Y *= rhs.Y;
+        Z *= rhs.Z;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector3& operator*=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X *= val;
+        Y *= val;
+        Z *= val;
+
+        return *this;
+    }
+
+    Vector3 operator*(const Vector3& rhs) const
+    {
+        return {X * rhs.X, Y * rhs.Y, Z * rhs.Z};
+    }
+
+    template <typename T>
+    Vector3 operator*(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X * val, Y * val, Z * val};
+    }
+
+    // ---
+
+    Vector3& operator/=(const Vector3& rhs)
+    {
+        X /= rhs.X;
+        Y /= rhs.Y;
+        Z /= rhs.Z;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector3& operator/=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X /= val;
+        Y /= val;
+        Z /= val;
+
+        return *this;
+    }
+
+    Vector3 operator/(const Vector3& rhs) const
+    {
+        return {X / rhs.X, Y / rhs.Y, Z / rhs.Z};
+    }
+
+    template <typename T>
+    Vector3 operator/(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X / val, Y / val, Z / val};
+    }
+
+    // --- public methods ---
+
+    Vector3 crossProduct(const Vector3& vec) const
+    {
+        return {Y * vec.Z - Z * vec.Y, Z * vec.X - X * vec.Z, X * vec.Y - Y * vec.X};
+    }
+
+    MT distanceToLine(const Vector3& origin, const Vector3& direction) const
+    {
+         if (direction.isNull())
+            return (*this - origin).length();
+
+        return (*this - (origin + dotProduct(*this - origin, direction) * direction)).length();
+    }
+
+    MT distanceToLinePoints(const Vector3& point1, const Vector3& point2) const
+    {
+        const Vector3 direction = point2 - point1;
+
+        if (direction.isNull())
+            return (*this - point1).length();
+
+        return (*this - (point1 + dotProduct(*this - point1, direction) * direction)).length();
+    }
+
+    MT distanceToPlane(const Vector3& origin, const Vector3& normal) const
+    {
+        return dotProduct(*this - origin, normal);
+    }
+
+    MT distanceToPlane(const Vector3& point1, const Vector3& point2, const Vector3& point3) const
+    {
+        return dotProduct(*this - point1, Vector3::normal(point2 - point1, point3 - point1));
+    }
+
+    MT distanceToPoint(const Vector3& point) const
+    {
+        return (*this - point).length();
+    }
+
+    MT dotProduct(const Vector3& vec) const
+    {
+        return X * vec.X + Y * vec.Y + Z * vec.Z;
+    }
+
+    bool isNull() const
+    {
+        return X == 0 && Y == 0 && Z == 0;
+    }
+
+    MT length() const
+    {
+        return std::sqrt(X * X + Y * Y + Z * Z);
+    }
+
+    MT lengthSquared() const
+    {
+        return X * X + Y * Y + Z * Z;
+    }
+
+    Vector3 normal(const Vector3& vec) const
+    {
+        return crossProduct(vec).normalized();
+    }
+
+    Vector3 normal(const Vector3& point2, const Vector3& point3) const
+    {
+        return crossProduct(point2 - *this, point3 - *this).normalized();
+    }
+
+    void normalize()
+    {
+        *this /= std::sqrt(X * X + Y * Y + Z * Z);
+    }
+
+    Vector3 normalized() const
+    {
+        const MT len = std::sqrt(X * X + Y * Y + Z * Z);
+
+        return {X / len, Y / len, Z / len};
+    }
+
+    std::string string() const
+    {
+        std::stringstream result;
+
+        result << X << ", " << Y << ", " << Z;
+
+        return result.str();
+    }
+
+    Vector2<MT> vector2() const
+    {
+        return {X, Y};
+    }
+
+    Vector4<MT> vector4() const
+    {
+        return {X, Y, Z, 0};
+    }
+
+    // --- static methods ---
+
+    static Vector3 toCrossProduct(const Vector3& vec1, const Vector3& vec2)
+    {
+        return {vec1.Y * vec2.Z - vec1.Z * vec2.Y,
+                vec1.Z * vec2.X - vec1.X * vec2.Z,
+                vec1.X * vec2.Y - vec1.Y * vec2.X};
+    }
+
+    static MT toDotProduct(const Vector3& vec1, const Vector3& vec2)
+    {
+        return vec1.X * vec2.X + vec1.Y * vec2.Y + vec1.Z * vec2.Z;
+    }
+
+    static Vector3 toNormal(const Vector3& vec1, const Vector3& vec2)
+    {
+        return toCrossProduct(vec1, vec2).normalized();
+    }
+
+    static Vector3 toNormal(const Vector3& point1, const Vector3& point2, const Vector3& point3)
+    {
+        return toCrossProduct(point2 - point1, point3 - point1).normalized();
+    }
+
+    static std::string toString(const Vector3& vec)
+    {
+        std::stringstream result;
+
+        result << vec.X << ", " << vec.Y << ", " << vec.Z;
+
+        return result.str();
+    }
+
+    static Vector2<MT> toVector2(const Vector3& vec)
+    {
+        return {vec.X, vec.Y};
+    }
+
+    static Vector4<MT> toVector4(const Vector3& vec)
+    {
+        return {vec.X, vec.Y, vec.Z, 0};
+    }
+};
+
+// --- global operators ---
+
+template <typename T>
+bool operator==(const Vector3<T>& lhs, const Vector3<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z;
+}
+
+template <typename T>
+bool operator!=(const Vector3<T> lhs, const Vector3<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return !(lhs == rhs);
+}
+
+template <typename T>
+std::ostream& operator<<(std::ostream& lhs, const Vector3<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs << rhs.X << rhs.Y << rhs.Z;
+
+    return lhs;
+}
+
+template <typename T>
+std::istream& operator>>(std::istream& lhs, Vector3<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs >> rhs.X >> rhs.Y >> rhs.Z;
+
+    return lhs;
+}
+
+// ---
+
+template <typename T>
+Vector3<T> operator+(const Vector3<T>& vec1, const Vector3<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X + vec2.X, vec1.Y + vec2.Y, vec1.Z + vec2.Z};
+}
+
+template <typename Vec, typename Val>
+Vector3<Vec> operator+(const Vector3<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X + val, vec.Y + val, vec.Z + val};
+}
+
+template <typename Val, typename Vec>
+Vector3<Vec> operator+(const Val& val, const Vector3<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val + vec.X, val + vec.Y, val + vec.Z};
+}
+
+// ---
+
+template <typename T>
+Vector3<T> operator-(const Vector3<T>& vec1, const Vector3<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X - vec2.X, vec1.Y - vec2.Y, vec1.Z - vec2.Z};
+}
+
+template <typename Vec, typename Val>
+Vector3<Vec> operator-(const Vector3<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X - val, vec.Y - val, vec.Z - val};
+}
+
+template <typename Val, typename Vec>
+Vector3<Vec> operator-(const Val& val, const Vector3<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val - vec.X, val - vec.Y, val - vec.Z};
+}
+
+template <typename T>
+Vector3<T> operator-(const Vector3<T>& vec)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {-vec.X, -vec.Y, -vec.Z};
+}
+
+// ---
+
+template <typename T>
+Vector3<T> operator*(const Vector3<T>& vec1, const Vector3<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X * vec2.X, vec1.Y * vec2.Y, vec1.Z * vec2.Z};
+}
+
+template <typename Vec, typename Val>
+Vector3<Vec> operator*(const Vector3<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X * val, vec.Y * val, vec.Z * val};
+}
+
+template <typename Val, typename Vec>
+Vector3<Vec> operator*(const Val& val, const Vector3<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val * vec.X, val + vec.Y, val * vec.Z};
+}
+
+// ---
+
+template <typename T>
+Vector3<T> operator/(const Vector3<T>& vec1, const Vector3<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X / vec2.X, vec1.Y / vec2.Y, vec1.Z / vec2.Z};
+}
+
+template <typename Vec, typename Val>
+Vector3<Vec> operator/(const Vector3<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X / val, vec.Y / val, vec.Z / val};
+}
+
+template <typename Val, typename Vec>
+Vector3<Vec> operator/(const Val& val, const Vector3<Vec> vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val / vec.X, val / vec.Y, val / vec.Z};
+}
+
+// ****************************************************************************************************************** //
+
+template <typename MT>
+class Vector4 {
+    static_assert (std::is_integral<MT>::value || std::is_floating_point<MT>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+public:
+    // --- data structures ---
+
+    union {
+        struct {
+            MT X;
+            MT Y;
+            MT Z;
+            MT W;
+        };
+        MT XYZW[4];
+    } __attribute__((packed));
+
+    // --- constructors and deconstructors ---
+
+    Vector4()
+    : XYZW{0, 0, 0, 0}
+    {
+    }
+
+    Vector4(const MT& val)
+    : XYZW{val, val, val, val}
+    {
+    }
+
+    Vector4(const MT& x, const MT& y, const MT& z, const MT& w)
+    : XYZW{x, y, z, w}
+    {
+    }
+
+    Vector4(const Vector2<MT>& vec)
+    : XYZW{vec.X, vec.Y, 0, 0}
+    {
+    }
+
+    Vector4(const Vector2<MT>& vec, const MT& val1, const MT& val2)
+    : XYZW{vec.X, vec.Y, val1, val2}
+    {
+    }
+
+    Vector4(const Vector2<MT>& vec1, const Vector2<MT>& vec2)
+    : XYZW{vec1.X, vec1.Y, vec2.X, vec2.Y}
+    {
+    }
+
+    Vector4(const Vector3<MT>& vec)
+    : XYZW{vec.X, vec.Y, vec.Z, 0}
+    {
+    }
+
+    Vector4(const Vector3<MT>& vec, const MT& val)
+    : XYZW{vec.X, vec.Y, vec.Z, val}
+    {
+    }
+
+    Vector4(const Vector4& rhs)
+    : XYZW{rhs.X, rhs.Y, rhs.Z, rhs.W}
+    {
+    }
+
+    Vector4(const Vector4&& rhs)
+    : XYZW{std::move(rhs.X), std::move(rhs.Y), std::move(rhs.Z), std::move(rhs.W)}
+    {
+    }
+
+    virtual ~Vector4()
+    {
+    }
+
+    // --- default operators ---
+
+    Vector4& operator=(const Vector4& rhs)
+    {
+        if (this != &rhs)
+        {
+            X = rhs.X;
+            Y = rhs.Y;
+            Z = rhs.Z;
+            W = rhs.W;
+        }
+
+        return *this;
+    }
+
+    Vector4& operator=(const Vector4&& rhs)
+    {
+        if (this != &rhs)
+        {
+            X = std::move(rhs.X);
+            Y = std::move(rhs.Y);
+            Z = std::move(rhs.Z);
+            W = std::move(rhs.W);
+        }
+
+        return *this;
+    }
+
+    bool operator==(const Vector4& rhs) const
+    {
+        return X == rhs.X && Y == rhs.Y && Z == rhs.Z && W == rhs.W;
+    }
+
+    bool operator!=(const Vector4& rhs) const
+    {
+        return !(*this == rhs);
+    }
+
+    const MT operator[](const size_t& index) const
+    {
+        return XYZW[index];
+    }
+
+    MT& operator[](const size_t& index)
+    {
+        return XYZW[index];
+    }
+
+    Vector4& operator~()
+    {
+        X = -X;
+        Y = -Y;
+        Z = -Z;
+        W = -W;
+
+        return *this;
+    }
+
+    Vector4 operator-() const
+    {
+        return {-X, -Y, -Z, -W};
+    }
+
+    // --- other operators ---
+
+    Vector4& operator+=(const Vector4& rhs)
+    {
+        X += rhs.X;
+        Y += rhs.Y;
+        Z += rhs.Z;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector4& operator+=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X += val;
+        Y += val;
+        Z += val;
+        W += val;
+
+        return *this;
+    }
+
+    Vector4 operator+(const Vector4& rhs) const
+    {
+        return {X + rhs.X, Y + rhs.Y, Z + rhs.Z, W + rhs.W};
+    }
+
+    template <typename T>
+    Vector4 operator+(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X + val, Y + val, Z + val, W + val};
+    }
+
+    // ---
+
+    Vector4& operator-=(const Vector4& rhs)
+    {
+        X -= rhs.X;
+        Y -= rhs.Y;
+        Z -= rhs.Z;
+        W -= rhs.W;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector4& operator-=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X -= val;
+        Y -= val;
+        Z -= val;
+        W -= val;
+
+        return *this;
+    }
+
+    Vector4 operator-(const Vector4& rhs) const
+    {
+        return {X - rhs.X, Y - rhs.Y, Z - rhs.Z, W - rhs.W};
+    }
+
+    template <typename T>
+    Vector4 operator-(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X - val, Y - val, Z - val, W - val};
+
+    }
+
+    // ---
+
+    Vector4& operator*=(const Vector4& rhs)
+    {
+        X *= rhs.X;
+        Y *= rhs.Y;
+        Z *= rhs.Z;
+        W *= rhs.W;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector4& operator*=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X *= val;
+        Y *= val;
+        Z *= val;
+        W *= val;
+
+        return *this;
+    }
+
+    Vector4 operator*(const Vector4& rhs) const
+    {
+        return {X * rhs.X, Y * rhs.Y, Z * rhs.Z, W * rhs.W};
+    }
+
+    template <typename T>
+    Vector4 operator*(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X * val, Y * val, Z * val, W * val};
+    }
+
+    // ---
+
+    Vector4& operator/=(const Vector4& rhs)
+    {
+        X /= rhs.X;
+        Y /= rhs.Y;
+        Z /= rhs.Z;
+        W /= rhs.W;
+
+        return *this;
+    }
+
+    template <typename T>
+    Vector4& operator/=(const T& val)
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        X /= val;
+        Y /= val;
+        Z /= val;
+        W /= val;
+
+        return *this;
+    }
+
+    Vector4 operator/(const Vector4& rhs) const
+    {
+        return {X / rhs.X, Y / rhs.Y, Z / rhs.Z, W / rhs.W};
+    }
+
+    template <typename T>
+    Vector4 operator/(const T& val) const
+    {
+        static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                       "ERROR: template parameter is not an integral or floating point type");
+
+        return {X / val, Y / val, Z / val, W / val};
+    }
+
+    // --- public methods ---
+
+    MT dotProduct(const Vector4& vec) const
+    {
+        return X * vec.X + Y * vec.Y + Z * vec.Z + W * vec.W;
+    }
+
+    bool isNull() const
+    {
+        return X == 0 && Y == 0 && Z == 0 && W == 0;
+    }
+
+    MT length() const
+    {
+        return std::sqrt(X * X + Y * Y + Z * Z + W * W);
+    }
+
+    MT lengthSquared() const
+    {
+        return X * X + Y * Y + Z * Z + W * W;
+    }
+
+    void normalize()
+    {
+        *this /= std::sqrt(X * X + Y * Y + Z * Z + W * W);
+    }
+
+    Vector4 normalized() const
+    {
+        const MT len = std::sqrt(X * X + Y * Y + Z * Z + W * W);
+
+        return {X / len, Y / len, Z / len, W / len};
+    }
+
+    std::string string() const
+    {
+        std::stringstream result;
+
+        result << X << ", " << Y << ", " << Z << ", " << W;
+
+        return result.str();
+    }
+
+    Vector2<MT> vector2() const
+    {
+        return {X, Y};
+    }
+
+    Vector3<MT> vector3() const
+    {
+        return {X, Y, Z};
+    }
+
+    // --- static methods ---
+
+    static MT toDotProduct(const Vector4& vec1, const Vector4& vec2)
+    {
+        return vec1.X * vec2.X + vec1.Y * vec2.Y + vec1.Z * vec2.Z + vec1.W * vec2.W;
+    }
+
+    static std::string toString(const Vector4& vec)
+    {
+        std::stringstream result;
+
+        result << vec.X << ", " << vec.Y << ", " << vec.Z << ", " << vec.W;
+
+        return result.str();
+    }
+
+    static Vector2<MT> toVector2(const Vector4& vec)
+    {
+        return {vec.X, vec.Y};
+    }
+
+    static Vector3<MT> toVector3(const Vector4& vec)
+    {
+        return {vec.X, vec.X, vec.Z};
+    }
+};
+
+// --- global operators ---
+
+template <typename T>
+bool operator==(const Vector4<T>& lhs, const Vector4<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return lhs.X == rhs.X && lhs.Y == rhs.Y && lhs.Z == rhs.Z && lhs.W == rhs.W;
+}
+
+template <typename T>
+bool operator!=(const Vector4<T> lhs, const Vector4<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return !(lhs == rhs);
+}
+
+template <typename T>
+std::ostream& operator<<(std::ostream& lhs, const Vector4<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs << rhs.X << rhs.Y << rhs.Z << rhs.W;
+
+    return lhs;
+}
+
+template <typename T>
+std::istream& operator>>(std::istream& lhs, Vector4<T>& rhs)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    lhs >> rhs.X >> rhs.Y >> rhs.Z >> rhs.W;
+
+    return lhs;
+}
+
+// ---
+
+template <typename T>
+Vector4<T> operator+(const Vector4<T>& vec1, const Vector4<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X + vec2.X, vec1.Y + vec2.Y, vec1.Z + vec2.Z, vec1.W + vec2.W};
+}
+
+template <typename Vec, typename Val>
+Vector4<Vec> operator+(const Vector4<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X + val, vec.Y + val, vec.Z + val, vec.W + val};
+}
+
+template <typename Val, typename Vec>
+Vector4<Vec> operator+(const Val& val, const Vector4<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val + vec.X, val + vec.Y, val + vec.Z, val + vec.W};
+}
+
+// ---
+
+template <typename T>
+Vector4<T> operator-(const Vector4<T>& vec1, const Vector4<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X - vec2.X, vec1.Y - vec2.Y, vec1.Z - vec2.Z, vec1.W - vec2.W};
+}
+
+template <typename Vec, typename Val>
+Vector4<Vec> operator-(const Vector4<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X - val, vec.Y - val, vec.Z - val, vec.W - val};
+}
+
+template <typename Val, typename Vec>
+Vector4<Vec> operator-(const Val& val, const Vector4<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val - vec.X, val - vec.Y, val - vec.Z, val - vec.W};
+}
+
+template <typename T>
+Vector4<T> operator-(const Vector4<T>& vec)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {-vec.X, -vec.Y, -vec.Z, -vec.W};
+}
+
+// ---
+
+template <typename T>
+Vector4<T> operator*(const Vector4<T>& vec1, const Vector4<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X * vec2.X, vec1.Y * vec2.Y, vec1.Z * vec2.Z, vec1.W * vec2.W};
+}
+
+template <typename Vec, typename Val>
+Vector4<Vec> operator*(const Vector4<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X * val, vec.Y * val, vec.Z * val, vec.W * val};
+}
+
+template <typename Val, typename Vec>
+Vector4<Vec> operator*(const Val& val, const Vector4<Vec>& vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val * vec.X, val + vec.Y, val * vec.Z, val * vec.W};
+}
+
+// ---
+
+template <typename T>
+Vector4<T> operator/(const Vector4<T>& vec1, const Vector4<T>& vec2)
+{
+    static_assert (std::is_integral<T>::value || std::is_floating_point<T>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec1.X / vec2.X, vec1.Y / vec2.Y, vec1.Z / vec2.Z, vec1.W / vec2.W};
+}
+
+template <typename Vec, typename Val>
+Vector4<Vec> operator/(const Vector4<Vec>& vec, const Val& val)
+{
+    static_assert (std::is_integral<Vec>::value || std::is_floating_point<Vec>::value ||
+                   std::is_integral<Val>::value || std::is_floating_point<Val>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {vec.X / val, vec.Y / val, vec.Z / val, vec.W / val};
+}
+
+template <typename Val, typename Vec>
+Vector4<Vec> operator/(const Val& val, const Vector4<Vec> vec)
+{
+    static_assert (std::is_integral<Val>::value || std::is_floating_point<Val>::value ||
+                   std::is_integral<Vec>::value || std::is_floating_point<Vec>::value,
+                   "ERROR: template parameter is not an integral or floating point type");
+
+    return {val / vec.X, val / vec.Y, val / vec.Z, val / vec.W};
+}