Browse Source

vectors with variable dimension

Kajetan Johannes Hammerle 3 years ago
parent
commit
d1120cb403
6 changed files with 367 additions and 1 deletions
  1. 2 0
      Main.cpp
  2. 52 0
      math/Vector.cpp
  3. 130 0
      math/Vector.h
  4. 1 1
      meson.build
  5. 174 0
      tests/VectorTests.cpp
  6. 8 0
      tests/VectorTests.h

+ 2 - 0
Main.cpp

@@ -6,6 +6,7 @@
 #include "tests/RandomTests.h"
 #include "tests/RingBufferTests.h"
 #include "tests/SplitStringTests.h"
+#include "tests/VectorTests.h"
 
 int main() {
     ArrayTests::test();
@@ -16,5 +17,6 @@ int main() {
     RandomTests::test();
     RingBufferTests::test();
     SplitStringTests::test();
+    VectorTests::test();
     return 0;
 }

+ 52 - 0
math/Vector.cpp

@@ -0,0 +1,52 @@
+#include <cmath>
+
+#include "math/Vector.h"
+
+template<>
+Vector<3>& Vector<3>::set(float x, float y, float z) {
+    values[0] = x;
+    values[1] = y;
+    values[2] = z;
+    return *this;
+}
+
+template<>
+Vector<3>::Vector(float x, float y, float z) {
+    set(x, y, z);
+}
+
+template<>
+Vector<2>& Vector<2>::set(float x, float y) {
+    values[0] = x;
+    values[1] = y;
+    return *this;
+}
+
+template<>
+Vector<2>::Vector(float x, float y) {
+    set(x, y);
+}
+
+template<>
+Vector<3>& Vector<3>::setAngles(float lengthAngle, float widthAngle) {
+    lengthAngle *= (M_PI / 180.0f);
+    widthAngle *= (M_PI / 180.0f);
+
+    float sinWidth;
+    float cosWidth;
+    sincosf(widthAngle, &sinWidth, &cosWidth);
+
+    float sinLength;
+    float cosLength;
+    sincosf(lengthAngle, &sinLength, &cosLength);
+
+    return set(cosWidth * cosLength, sinWidth, -sinLength * cosWidth);
+}
+
+template<>
+Vector<3> Vector<3>::cross(const Vector<3>& other) const {
+    return Vector<3>(
+            values[1] * other.values[2] - values[2] * other.values[1],
+            values[2] * other.values[0] - values[0] * other.values[2],
+            values[0] * other.values[1] - values[1] * other.values[0]);
+}

+ 130 - 0
math/Vector.h

@@ -0,0 +1,130 @@
+#ifndef VECTOR_H
+#define VECTOR_H
+
+#include "utils/StringBuffer.h"
+
+template<int N>
+class Vector final {
+    float values[N];
+
+public:
+
+    Vector() {
+        for(int i = 0; i < N; i++) {
+            values[i] = 0.0f;
+        }
+    }
+
+    Vector(float, float) = delete;
+    Vector(float, float, float) = delete;
+
+    Vector& set(float, float) = delete;
+    Vector& set(float, float, float) = delete;
+
+    Vector& setAngles(float, float) = delete;
+    Vector cross(const Vector&) const = delete;
+
+    Vector& operator+=(const Vector& other) {
+        for(int i = 0; i < N; i++) {
+            values[i] += other.values[i];
+        }
+        return *this;
+    }
+
+    Vector operator+(const Vector& other) const {
+        Vector v = *this;
+        v += other;
+        return v;
+    }
+
+    Vector& operator-=(const Vector& other) {
+        for(int i = 0; i < N; i++) {
+            values[i] -= other.values[i];
+        }
+        return *this;
+    }
+
+    Vector operator-() const {
+        Vector v = *this;
+        for(int i = 0; i < N; i++) {
+            v.values[i] = -v.values[i];
+        }
+        return v;
+    }
+
+    Vector operator-(const Vector& other) const {
+        Vector v = *this;
+        v -= other;
+        return v;
+    }
+
+    Vector& operator*=(float factor) {
+        for(int i = 0; i < N; i++) {
+            values[i] *= factor;
+        }
+        return *this;
+    }
+
+    Vector operator*(float factor) const {
+        Vector v = *this;
+        v *= factor;
+        return v;
+    }
+
+    float dot(const Vector& v) const {
+        float length = 0.0f;
+        for(int i = 0; i < N; i++) {
+            length += values[i] * v.values[i];
+        }
+        return length;
+    }
+
+    float squareLength() const {
+        return dot(*this);
+    }
+
+    float length() const {
+        return sqrtf(squareLength());
+    }
+
+    Vector& normalize() {
+        *this *= 1.0f / length();
+        return *this;
+    }
+
+    const float& operator[](int index) const {
+        return values[index];
+    }
+
+    template<int L>
+    void toString(StringBuffer<L>& s) const {
+        s.append("[");
+        for(int i = 0; i < N - 1; i++) {
+            s.append(values[i]);
+            s.append(", ");
+        }
+        if(N > 0) {
+            s.append(values[N - 1]);
+        }
+        s.append("]");
+    }
+};
+
+template<int N>
+Vector<N> operator*(float factor, const Vector<N>& v) {
+    return v * factor;
+}
+
+template<> Vector<3>::Vector(float x, float y, float z);
+template<> Vector<2>::Vector(float x, float y);
+
+template<> Vector<3>& Vector<3>::set(float x, float y, float z);
+template<> Vector<2>& Vector<2>::set(float x, float y);
+
+template<> Vector<3>& Vector<3>::setAngles(float lengthAngle, float widthAngle);
+template<> Vector<3> Vector<3>::cross(const Vector<3>& other) const;
+
+typedef Vector<3> Vector3;
+typedef Vector<2> Vector2;
+
+#endif

+ 1 - 1
meson.build

@@ -1,6 +1,6 @@
 project('gaming core tests', 'cpp')
 
-sources = ['Main.cpp', 'tests/Test.cpp', 'tests/ArrayTests.cpp', 'tests/HashMapTests.cpp', 'tests/ListTests.cpp', 'tests/BitArrayTests.cpp', 'tests/StringBufferTests.cpp', 'tests/RandomTests.cpp', 'utils/Random.cpp', 'tests/RingBufferTests.cpp', 'tests/SplitStringTests.cpp']
+sources = ['Main.cpp', 'tests/Test.cpp', 'tests/ArrayTests.cpp', 'tests/HashMapTests.cpp', 'tests/ListTests.cpp', 'tests/BitArrayTests.cpp', 'tests/StringBufferTests.cpp', 'tests/RandomTests.cpp', 'utils/Random.cpp', 'tests/RingBufferTests.cpp', 'tests/SplitStringTests.cpp', 'tests/VectorTests.cpp', 'math/Vector.cpp']
 
 executable('tests', 
     sources: sources,

+ 174 - 0
tests/VectorTests.cpp

@@ -0,0 +1,174 @@
+#include <math.h>
+
+#include "tests/VectorTests.h"
+#include "tests/Test.h"
+#include "utils/StringBuffer.h"
+#include "math/Vector.h"
+
+const float eps = 0.0001f;
+
+template<int N>
+static void compareVectors(Test& test, const Vector<N>& wanted, const Vector<N>& actual, const char* text) {
+    for(int i = 0; i < N; i++) {
+        test.checkFloat(wanted[i], actual[i], eps, StringBuffer<50>(text).append(" (").append(i).append(")"));
+    }
+}
+
+static void testInitAndRead(Test& test) {
+    Vector3 v1;
+    Vector2 v2(1.0f, 2.0f);
+    Vector3 v3(3.0f, 4.0f, 5.0f);
+    test.checkEqual(0.0f, v1[0], "init 1");
+    test.checkEqual(0.0f, v1[0], "init 2");
+    test.checkEqual(0.0f, v1[0], "init 3");
+    test.checkEqual(1.0f, v2[0], "init 4");
+    test.checkEqual(2.0f, v2[1], "init 5");
+    test.checkEqual(3.0f, v3[0], "init 6");
+    test.checkEqual(4.0f, v3[1], "init 7");
+    test.checkEqual(5.0f, v3[2], "init 8");
+}
+
+static void testSetAngles(Test& test) {
+    float root = sqrtf(2.0f) * 0.5f;
+    compareVectors(test, Vector3(1.0f, 0.0f, 0.0f), Vector3().setAngles(0.0f, 0.0f), "angle 0 | 0");
+    compareVectors(test, Vector3(root, 0.0f, -root), Vector3().setAngles(45.0f, 0.0f), "angle 45 | 0");
+    compareVectors(test, Vector3(0.0f, 0.0f, -1.0f), Vector3().setAngles(90.0f, 0.0f), "angle 90 | 0");
+    compareVectors(test, Vector3(-root, 0.0f, -root), Vector3().setAngles(135.0f, 0.0f), "angle 135 | 0");
+    compareVectors(test, Vector3(-1.0f, 0.0f, 0.0f), Vector3().setAngles(180.0f, 0.0f), "angle 180 | 0");
+    compareVectors(test, Vector3(-root, 0.0f, root), Vector3().setAngles(225.0f, 0.0f), "angle 225 | 0");
+    compareVectors(test, Vector3(0.0f, 0.0f, 1.0f), Vector3().setAngles(270.0f, 0.0f), "angle 270 | 0");
+    compareVectors(test, Vector3(root, 0.0f, root), Vector3().setAngles(315.0f, 0.0f), "angle 315 | 0");
+
+    compareVectors(test, Vector3(0.0f, 1.0f, 0.0f), Vector3().setAngles(0.0f, 90.0f), "angle 0 | 90");
+    compareVectors(test, Vector3(0.0f, 1.0f, 0.0f), Vector3().setAngles(90.0f, 90.0f), "angle 90 | 90");
+    compareVectors(test, Vector3(0.0f, 1.0f, 0.0f), Vector3().setAngles(180.0f, 90.0f), "angle 180 | 90");
+    compareVectors(test, Vector3(0.0f, 1.0f, 0.0f), Vector3().setAngles(270.0f, 90.0f), "angle 270 | 90");
+
+    compareVectors(test, Vector3(0.0f, -1.0f, 0.0f), Vector3().setAngles(0.0f, -90.0f), "angle 0 | -90");
+    compareVectors(test, Vector3(0.0f, -1.0f, 0.0f), Vector3().setAngles(90.0f, -90.0f), "angle 90 | -90");
+    compareVectors(test, Vector3(0.0f, -1.0f, 0.0f), Vector3().setAngles(180.0f, -90.0f), "angle 180 | -90");
+    compareVectors(test, Vector3(0.0f, -1.0f, 0.0f), Vector3().setAngles(270.0f, -90.0f), "angle 270 | -90");
+
+    compareVectors(test, Vector3(root, root, 0.0f), Vector3().setAngles(0.0f, 45.0f), "angle 0 | 45");
+    compareVectors(test, Vector3(0.0f, root, -root), Vector3().setAngles(90.0f, 45.0f), "angle 90 | 45");
+    compareVectors(test, Vector3(-root, root, 0.0f), Vector3().setAngles(180.0f, 45.0f), "angle 180 | 45");
+    compareVectors(test, Vector3(0.0f, root, root), Vector3().setAngles(270.0f, 45.0f), "angle 270 | 45");
+
+    compareVectors(test, Vector3(root, -root, 0.0f), Vector3().setAngles(0.0f, -45.0f), "angle 0 | -45");
+    compareVectors(test, Vector3(0.0f, -root, -root), Vector3().setAngles(90.0f, -45.0f), "angle 90 | -45");
+    compareVectors(test, Vector3(-root, -root, 0.0f), Vector3().setAngles(180.0f, -45.0f), "angle 180 | -45");
+    compareVectors(test, Vector3(0.0f, -root, root), Vector3().setAngles(270.0f, -45.0f), "angle 270 | -45");
+
+    compareVectors(test, Vector3(0.5f, root, -0.5f), Vector3().setAngles(45.0f, 45.0f), "angle 45 | 45");
+}
+
+static void testCross(Test& test) {
+    compareVectors(test, Vector3(0.0f, 0.0f, 1.0f),
+            Vector3(1.0f, 0.0f, 0.0f).cross(Vector3(0.0f, 1.0f, 0.0)), "cross 1");
+    compareVectors(test, Vector3(0.0f, -1.0f, 0.0f),
+            Vector3(1.0f, 0.0f, 0.0f).cross(Vector3(0.0f, 0.0f, 1.0)), "cross 2");
+    compareVectors(test, Vector3(0.0f, 0.0f, -1.0f),
+            Vector3(0.0f, 1.0f, 0.0f).cross(Vector3(1.0f, 0.0f, 0.0)), "cross 3");
+    compareVectors(test, Vector3(1.0f, 0.0f, 0.0f),
+            Vector3(0.0f, 1.0f, 0.0f).cross(Vector3(0.0f, 0.0f, 1.0)), "cross 4");
+    compareVectors(test, Vector3(0.0f, 1.0f, 0.0f),
+            Vector3(0.0f, 0.0f, 1.0f).cross(Vector3(1.0f, 0.0f, 0.0)), "cross 5");
+    compareVectors(test, Vector3(-1.0f, 0.0f, 0.0f),
+            Vector3(0.0f, 0.0f, 1.0f).cross(Vector3(0.0f, 1.0f, 0.0)), "cross 6");
+}
+
+static void testSetAdd(Test& test) {
+    Vector3 v;
+    v += Vector3(1.0f, 2.0f, 3.0f);
+    compareVectors(test, Vector3(1.0f, 2.0f, 3.0f), v, "set add 1");
+    v += Vector3(2.0f, 3.0f, 4.0f);
+    compareVectors(test, Vector3(3.0f, 5.0f, 7.0f), v, "set add 2");
+}
+
+static void testAdd(Test& test) {
+    compareVectors(test, Vector3(1.0f, 2.0f, 3.0f), Vector3() + Vector3(1.0f, 2.0f, 3.0f), "add 1");
+    compareVectors(test, Vector3(3.0f, 5.0f, 7.0f), Vector3(1.0f, 2.0f, 3.0f) + Vector3(2.0f, 3.0f, 4.0f), "add 2");
+}
+
+static void testSetSub(Test& test) {
+    Vector3 v;
+    v -= Vector3(1.0f, 2.0f, 3.0f);
+    compareVectors(test, Vector3(-1.0f, -2.0f, -3.0f), v, "set sub 1");
+    v -= Vector3(2.0f, 3.0f, 4.0f);
+    compareVectors(test, Vector3(-3.0f, -5.0f, -7.0f), v, "set sub 2");
+}
+
+static void testSub(Test& test) {
+    compareVectors(test, Vector3(1.0f, 2.0f, 3.0f), Vector3() - Vector3(-1.0f, -2.0f, -3.0f), "sub 1");
+    compareVectors(test, Vector3(-1.0f, -1.0f, -1.0f), Vector3(1.0f, 2.0f, 3.0f) - Vector3(2.0f, 3.0f, 4.0f), "sub 2");
+}
+
+static void testInvert(Test& test) {
+    compareVectors(test, Vector3(-1.0f, 2.0f, 3.0f), -Vector3(1.0f, -2.0f, -3.0f), "invert");
+}
+
+static void testSetMul(Test& test) {
+    Vector3 v(1.0f, 2.0f, 3.0f);
+    v *= 3.0f;
+    compareVectors(test, Vector3(3.0f, 6.0f, 9.0f), v, "set mul 1");
+    v *= -2.0f;
+    compareVectors(test, Vector3(-6.0f, -12.0f, -18.0f), v, "set mul 2");
+}
+
+static void testMul(Test& test) {
+    compareVectors(test, Vector3(-3.0f, -6.0f, -9.0f), 3.0f * Vector3(-1.0f, -2.0f, -3.0f), "mul 1");
+    compareVectors(test, Vector3(3.0f, 6.0f, 9.0f), Vector3(1.0f, 2.0f, 3.0f) * 3.0, "mul 2");
+}
+
+static void testDot(Test& test) {
+    test.checkFloat(9.0f, Vector3(-4.0f, 2.0f, -3.0f).dot(Vector3(-1.0f, -2.0f, -3.0f)), eps, "dot 1");
+    test.checkFloat(-22.0f, Vector3(2.0f, 2.0f, -4.0f).dot(Vector3(1.0f, -2.0f, 5.0f)), eps, "dot 2");
+}
+
+static void testSquareLength(Test& test) {
+    test.checkFloat(29.0f, Vector3(-4.0f, 2.0f, -3.0f).squareLength(), eps, "square length 1");
+    test.checkFloat(24.0f, Vector3(2.0f, 2.0f, -4.0f).squareLength(), eps, "square length 2");
+}
+
+static void testLength(Test& test) {
+    test.checkFloat(3.0f, Vector3(-2.0f, 2.0f, -1.0f).length(), eps, "length 1");
+    test.checkFloat(7.0f, Vector3(6.0f, 2.0f, -3.0f).length(), eps, "length 2");
+}
+
+static void testNormalize(Test& test) {
+    Vector3 v1(-2.0f, 2.0f, -1.0f);
+    Vector3 v2 = v1 * (1.0f / 3.0f);
+    v1.normalize();
+    compareVectors(test, v2, v1, "normalize 1");
+    
+    Vector3 v3(6.0f, 2.0f, -3.0f);
+    Vector3 v4 = v3 * (1.0f / 7.0f);
+    v3.normalize();
+    compareVectors(test, v4, v3, "normalize 2");
+}
+
+static void testToString(Test& test) {
+    StringBuffer<50> s;
+    s.append(Vector<1>()).append(Vector<2>(2.0f, 3.0f)).append(Vector<3>(4.0f, 5.0f, 6.0f));
+    test.checkEqual(StringBuffer<50>("[0.00][2.00, 3.00][4.00, 5.00, 6.00]"), s, "to string");
+}
+
+void VectorTests::test() {
+    Test test("Vector");
+    testInitAndRead(test);
+    testSetAngles(test);
+    testCross(test);
+    testSetAdd(test);
+    testAdd(test);
+    testSetSub(test);
+    testSub(test);
+    testInvert(test);
+    testSetMul(test);
+    testMul(test);
+    testDot(test);
+    testSquareLength(test);
+    testLength(test);
+    testNormalize(test);
+    testToString(test);
+    test.finalize();
+}

+ 8 - 0
tests/VectorTests.h

@@ -0,0 +1,8 @@
+#ifndef VECTORTESTS_H
+#define VECTORTESTS_H
+
+namespace VectorTests {
+    void test();
+}
+
+#endif