Kajetan Johannes Hammerle 4 kuukautta sitten
vanhempi
commit
d575a92e4f

+ 2 - 2
CMakeLists.txt

@@ -14,7 +14,7 @@ set(SRC
     "src/Logger.cpp"
     #"src/Matrix.cpp"
     #"src/Plane.cpp"
-    #"src/Quaternion.cpp"
+    "src/Quaternion.cpp"
     #"src/Random.cpp"
     #"src/ReadLine.cpp"
     #"src/SpinLock.cpp"
@@ -40,7 +40,7 @@ set(SRC_TESTS
     #"test/modules/ListTests.cpp"
     #"test/modules/MatrixTests.cpp"
     #"test/modules/PlaneTests.cpp"
-    #"test/modules/QuaternionTests.cpp"
+    "test/modules/QuaternionTests.cpp"
     #"test/modules/QueueTests.cpp"
     #"test/modules/RandomTests.cpp"
     #"test/modules/ReadLineTests.cpp"

+ 15 - 14
include/core/Quaternion.hpp

@@ -1,20 +1,21 @@
-#ifndef CORE_QUATERNION_H
-#define CORE_QUATERNION_H
+#ifndef CORE_QUATERNION_HPP
+#define CORE_QUATERNION_HPP
 
 #include "core/Vector.hpp"
 
-typedef struct {
-    Vector4 v;
-} Quaternion;
+namespace Core {
+    class Quaternion final {
+        Vector4 v;
 
-#define UNIT_QUATERNION ((Quaternion){{{0.0f, 0.0f, 0.0f, 1.0f}}})
-
-Quaternion* axisAngleQ(Quaternion* q, const Vector3* axis, float angle);
-Quaternion* lerpQ(
-    Quaternion* q, const Quaternion* a, float f, const Quaternion* b);
-Quaternion* mulSetQ(Quaternion* q, const Quaternion* other);
-Quaternion* mulQ(Quaternion* q, const Quaternion* a, const Quaternion* b);
-Vector3* mulQV3(Vector3* r, const Quaternion* q, const Vector3* v);
-size_t toStringQ(const Quaternion* q, char* buffer, size_t n);
+    public:
+        Quaternion();
+        Quaternion(const Vector3& axis, float angle);
+        Quaternion lerp(float f, const Quaternion& other) const;
+        Quaternion& operator*=(const Quaternion& other);
+        Quaternion operator*(const Quaternion& other) const;
+        Vector3 operator*(const Vector3& v) const;
+        size_t toString(char* s, size_t n) const;
+    };
+}
 
 #endif

+ 9 - 5
include/core/Test.hpp

@@ -26,12 +26,16 @@ namespace Core {
     bool testString(
         const char* file, int line, const char* wanted, const char* actual);
 
-    template<typename T>
+    template<typename A, typename B>
     bool testString(
-        const char* file, int line, const char* wanted, const T& t) {
-        char buffer[512];
-        toString(t, buffer, sizeof(buffer));
-        return testString(file, line, wanted, static_cast<const char*>(buffer));
+        const char* file, int line, const A& wanted, const B& actual) {
+        char wantedString[512];
+        toString(wanted, wantedString, sizeof(wantedString));
+        char actualString[512];
+        toString(actual, actualString, sizeof(actualString));
+        return testString(
+            file, line, static_cast<const char*>(wantedString),
+            static_cast<const char*>(actualString));
     }
 
     bool testFloat(

+ 13 - 0
include/core/Vector.hpp

@@ -6,6 +6,9 @@
 #include "core/Math.hpp"
 #include "core/ToString.hpp"
 
+template<size_t R>
+concept Wusi = R > 3;
+
 namespace Core {
     template<size_t N, typename T>
     class alignas(sizeof(T) * (Core::isPowerOf2(N) ? N : 1)) Vector final {
@@ -185,6 +188,16 @@ namespace Core {
             return true;
         }
 
+        template<Core::If<N >= 3, int, void*> = 0>
+        auto& xyz() {
+            return *reinterpret_cast<Vector<3, T>*>(this);
+        }
+
+        template<Core::If<N >= 3, int, void*> = 0>
+        const auto& xyz() const {
+            return *reinterpret_cast<const Vector<3, T>*>(this);
+        }
+
     private:
         template<typename O>
         static bool notEqual(const O& a, const O& b) {

+ 22 - 0
include/core/View.hpp

@@ -18,4 +18,26 @@ void updateDirections(View* v, float lengthAngle, float widthAngle);
 void updateDirectionsQ(View* v, const Quaternion* q);
 Matrix* updateMatrix(View* v, const Vector3* pos);
 
+#include "core/math/Matrix.hpp"
+
+namespace Core {
+    class View final {
+        Matrix view{};
+        Vector3 right{};
+        Vector3 up{};
+        Vector3 back{};
+
+    public:
+        void updateDirections(float lengthAngle, float widthAngle);
+        void updateDirections(const Quaternion& q);
+        const Matrix& updateMatrix(const Vector3& pos);
+        Vector3 getUp() const;
+        Vector3 getDown() const;
+        Vector3 getLeft() const;
+        Vector3 getRight() const;
+        Vector3 getFront() const;
+        Vector3 getBack() const;
+    };
+}
+
 #endif

+ 0 - 51
old/Quaternion.cpp

@@ -1,51 +0,0 @@
-#include "core/math/Quaternion.hpp"
-
-Core::Quaternion::Quaternion() : xyz(), w(1.0f) {
-}
-
-Core::Quaternion::Quaternion(const Vector3& axis, float angle)
-    : xyz(axis), w(1.0f) {
-    xyz.normalize();
-    float factor = 0.0f;
-    sincosf(Core::Math::degreeToRadian(angle) * 0.5f, &factor, &w);
-    xyz *= factor;
-}
-
-Core::Quaternion Core::Quaternion::lerp(float f,
-                                        const Quaternion& other) const {
-    Quaternion q;
-    q.xyz = xyz * (1.0f - f) + other.xyz * f;
-    q.w = w * (1.0f - f) + other.w * f;
-    float iLength = 1.0f / sqrtf(q.xyz.squareLength() + q.w * q.w);
-    q.xyz *= iLength;
-    q.w *= iLength;
-    return q;
-}
-
-Core::Quaternion& Core::Quaternion::operator*=(const Quaternion& other) {
-    float dot = xyz.dot(other.xyz);
-    xyz = other.xyz * w + xyz * other.w + xyz.cross(other.xyz);
-    w = w * other.w - dot;
-    return *this;
-}
-
-Core::Quaternion Core::Quaternion::operator*(const Quaternion& other) const {
-    Quaternion q(*this);
-    q *= other;
-    return q;
-}
-
-Core::Vector3 Core::Quaternion::operator*(const Vector3& v) const {
-    Vector3 qv = v * w + xyz.cross(v);
-    Vector3 qvq = xyz * xyz.dot(v) + qv * w - qv.cross(xyz);
-    return qvq;
-}
-
-void Core::Quaternion::toString(BufferString& s) const {
-    s.append("(");
-    s.append(xyz[0]).append(" i + ");
-    s.append(xyz[1]).append(" j + ");
-    s.append(xyz[2]).append(" k + ");
-    s.append(w);
-    s.append(')');
-}

+ 0 - 23
old/Quaternion.hpp

@@ -1,23 +0,0 @@
-#ifndef CORE_QUATERNION_HPP
-#define CORE_QUATERNION_HPP
-
-#include "core/math/Vector.hpp"
-#include "core/utils/ArrayString.hpp"
-
-namespace Core {
-    class Quaternion final {
-        Vector3 xyz;
-        float w;
-
-    public:
-        Quaternion();
-        Quaternion(const Vector3& axis, float angle);
-        Quaternion lerp(float f, const Quaternion& other) const;
-        Quaternion& operator*=(const Quaternion& other);
-        Quaternion operator*(const Quaternion& other) const;
-        Vector3 operator*(const Vector3& v) const;
-        void toString(BufferString& s) const;
-    };
-}
-
-#endif

+ 0 - 69
old/QuaternionTests.cpp

@@ -1,69 +0,0 @@
-#include "../Tests.hpp"
-#include "core/math/Quaternion.hpp"
-
-using V3 = Core::Vector3;
-
-static void testInit() {
-    Core::Quaternion q;
-    CORE_TEST_STRING("(0.00 i + 0.00 j + 0.00 k + 1.00)", q);
-}
-
-static void testAxisAndDegreesInit() {
-    Core::Quaternion q(V3(1.0f, 2.0f, 3.0f), 142.0f);
-    CORE_TEST_STRING("(0.25 i + 0.51 j + 0.76 k + 0.33)", q);
-}
-
-static void testLerp() {
-    Core::Quaternion q1(V3(2.0f, 5.0f, 7.0f), 130.0f);
-    Core::Quaternion q2(V3(1.0f, 2.0f, 4.0f), 260.0f);
-    Core::Quaternion q3 = q1.lerp(0.3f, q2);
-    CORE_TEST_STRING("(0.22 i + 0.53 j + 0.81 k + 0.12)", q3);
-}
-
-static void testMulSet() {
-    Core::Quaternion q1(V3(2.0f, 5.0f, 7.0f), 50.0f);
-    Core::Quaternion q2(V3(2.0f, 5.0f, 7.0f), 60.0f);
-    Core::Quaternion q3;
-    q3 *= q1;
-    CORE_TEST_STRING(q1, q3);
-    q3 *= q2;
-    CORE_TEST_STRING(Core::Quaternion(V3(2.0f, 5.0f, 7.0f), 110.0f), q3);
-}
-
-static void testMul() {
-    Core::Quaternion q1(V3(2.0f, 5.0f, 7.0f), 50.0f);
-    Core::Quaternion q2(V3(2.0f, 5.0f, 7.0f), 60.0f);
-    Core::Quaternion q3 = q1 * q2;
-    CORE_TEST_STRING(Core::Quaternion(V3(2.0f, 5.0f, 7.0f), 110.0f), q3);
-}
-
-static void testMulVector() {
-    Core::Quaternion q1(V3(1.0f, 0.0f, 0.0f), 90.0f);
-    Core::Quaternion q2(V3(0.0f, 1.0f, 0.0f), 90.0f);
-    Core::Quaternion q3(V3(0.0f, 0.0f, 1.0f), 90.0f);
-
-    V3 v1(1.0f, 0.0f, 0.0f);
-    V3 v2(0.0f, 1.0f, 0.0f);
-    V3 v3(0.0f, 0.0f, 1.0f);
-
-    CORE_TEST_VECTOR(V3(1.0f, 0.0f, 0.0f), q1 * v1);
-    CORE_TEST_VECTOR(V3(0.0f, 0.0f, 1.0f), q1 * v2);
-    CORE_TEST_VECTOR(V3(0.0f, -1.0f, 0.0f), q1 * v3);
-
-    CORE_TEST_VECTOR(V3(0.0f, 0.0f, -1.0f), q2 * v1);
-    CORE_TEST_VECTOR(V3(0.0f, 1.0f, 0.0f), q2 * v2);
-    CORE_TEST_VECTOR(V3(1.0f, 0.0f, 0.0f), q2 * v3);
-
-    CORE_TEST_VECTOR(V3(0.0f, 1.0f, 0.0f), q3 * v1);
-    CORE_TEST_VECTOR(V3(-1.0f, 0.0f, 0.0f), q3 * v2);
-    CORE_TEST_VECTOR(V3(0.0f, 0.0f, 1.0f), q3 * v3);
-}
-
-void Core::testQuaternion() {
-    testInit();
-    testAxisAndDegreesInit();
-    testLerp();
-    testMulSet();
-    testMul();
-    testMulVector();
-}

+ 0 - 26
old/View.hpp

@@ -1,26 +0,0 @@
-#ifndef CORE_VIEW_HPP
-#define CORE_VIEW_HPP
-
-#include "core/math/Matrix.hpp"
-
-namespace Core {
-    class View final {
-        Matrix view{};
-        Vector3 right{};
-        Vector3 up{};
-        Vector3 back{};
-
-    public:
-        void updateDirections(float lengthAngle, float widthAngle);
-        void updateDirections(const Quaternion& q);
-        const Matrix& updateMatrix(const Vector3& pos);
-        Vector3 getUp() const;
-        Vector3 getDown() const;
-        Vector3 getLeft() const;
-        Vector3 getRight() const;
-        Vector3 getFront() const;
-        Vector3 getBack() const;
-    };
-}
-
-#endif

+ 33 - 34
src/Quaternion.cpp

@@ -1,50 +1,49 @@
-#include <math.h>
+#include "core/Quaternion.hpp"
 
-#include "core/Generic.hpp"
-#include "core/ToString.hpp"
+#include <cmath>
 
-typedef Quaternion Q;
+Core::Quaternion::Quaternion() : v(0.0f, 0.0f, 0.0f, 1.0f) {
+}
 
-Q* axisAngleQ(Q* q, const Vector3* axis, float angle) {
-    q->v.xyz = *axis;
-    normalize(&q->v.xyz);
-    angle *= 0.5f;
-    q->v.w = cosf(angle);
-    mulSet(&q->v.xyz, sinf(angle));
-    return q;
+Core::Quaternion::Quaternion(const Vector3& axis, float angle) :
+    v(axis[0], axis[1], axis[2], 1.0f) {
+    v.xyz().normalize();
+    float factor = 0.0f;
+    sincosf(angle * 0.5f, &factor, &v[3]);
+    v.xyz() *= factor;
 }
 
-Q* lerpQ(Q* q, const Q* a, float f, const Q* b) {
-    add(&q->v, mul(&a->v, 1.0f - f), mul(&b->v, f));
-    float iLength = 1.0f / length(&q->v);
-    mulSet(&q->v, iLength);
+Core::Quaternion Core::Quaternion::lerp(
+    float f, const Quaternion& other) const {
+    Quaternion q;
+    q.v = interpolate(v, other.v, f);
+    q.v.normalize();
     return q;
 }
 
-Q* mulSetQ(Q* q, const Q* o) {
-    float dot = dot(&q->v.xyz, &o->v.xyz);
-    add(&q->v.xyz, mul(&o->v.xyz, q->v.w),
-        add(mul(&q->v.xyz, o->v.w), cross(&q->v.xyz, &o->v.xyz)));
-    q->v.w = q->v.w * o->v.w - dot;
-    return q;
+Core::Quaternion& Core::Quaternion::operator*=(const Quaternion& other) {
+    float dot = v.xyz().dot(other.v.xyz());
+    v.xyz() = other.v.xyz() * v[3] + v.xyz() * other.v[3] +
+              cross(v.xyz(), other.v.xyz());
+    v[3] = v[3] * other.v[3] - dot;
+    return *this;
 }
 
-Q* mulQ(Q* q, const Q* a, const Q* b) {
-    *q = *a;
-    mulSetQ(q, b);
+Core::Quaternion Core::Quaternion::operator*(const Quaternion& other) const {
+    Quaternion q(*this);
+    q *= other;
     return q;
 }
 
-Vector3* mulQV3(Vector3* r, const Q* q, const Vector3* v) {
-    Vector3 qv;
-    add(&qv, mul(v, q->v.w), cross(&q->v.xyz, v));
-    add(r, mul(&q->v.xyz, dot(&q->v.xyz, v)),
-        sub(mul(&qv, q->v.w), cross(&qv, &q->v.xyz)));
-    return r;
+Core::Vector3 Core::Quaternion::operator*(const Vector3& v3) const {
+    Vector3 qv = v3 * v[3] + cross(v.xyz(), v3);
+    return v.xyz() * v.xyz().dot(v3) + qv * v[3] - cross(qv, v.xyz());
 }
 
-size_t toStringQ(const Q* q, char* buffer, size_t n) {
-    return toString(
-        buffer, n, "(%.3f i + %.3f j + %.3f k + %.3f)", (double)q->v.x,
-        (double)q->v.y, (double)q->v.z, (double)q->v.w);
+size_t Core::Quaternion::toString(char* s, size_t n) const {
+    int w = snprintf(
+        s, n, "(%.3f i + %.3f j + %.3f k + %.3f)", static_cast<double>(v[0]),
+        static_cast<double>(v[1]), static_cast<double>(v[2]),
+        static_cast<double>(v[3]));
+    return w >= 0 ? static_cast<size_t>(w) : 0;
 }

+ 1 - 1
test/Main.cpp

@@ -75,7 +75,7 @@ int main(int argAmount, const char** args) {
     // testList(light);
     // testMatrix();
     // testPlane();
-    // testQuaternion();
+    testQuaternion();
     // testQueue();
     // testRandom(light);
     if(light) {

+ 42 - 80
test/modules/QuaternionTests.cpp

@@ -1,101 +1,63 @@
-#include "../Tests.h"
-#include "core/Generic.h"
-#include "core/Utility.h"
+#include "../Tests.hpp"
+#include "core/Quaternion.hpp"
+#include "core/Test.hpp"
 
-#define CV3(a, b, c) (&(Vector3){{a, b, c}})
-#define CV30 CV3(0.0f, 0.0f, 0.0f)
+using Q = Core::Quaternion;
+using V3 = Core::Vector3;
 
 static void testInit() {
-    Quaternion q = UNIT_QUATERNION;
-    char buffer[128];
-    toStringQ(&q, buffer, sizeof(buffer));
-    TEST_STRING("(0.000 i + 0.000 j + 0.000 k + 1.000)", buffer);
+    Q q;
+    TEST_STRING("(0.000 i + 0.000 j + 0.000 k + 1.000)", q);
 }
 
 static void testAxisAndDegreesInit() {
-    Quaternion q = UNIT_QUATERNION;
-    axisAngleQ(&q, CV3(1.0f, 2.0f, 3.0f), degreeToRadian(142.0f));
-    char buffer[128];
-    toStringQ(&q, buffer, sizeof(buffer));
-    TEST_STRING("(0.253 i + 0.505 j + 0.758 k + 0.326)", buffer);
+    Q q(V3(1.0f, 2.0f, 3.0f), Core::degreeToRadian(142.0f));
+    TEST_STRING("(0.253 i + 0.505 j + 0.758 k + 0.326)", q);
 }
 
 static void testLerp() {
-    Quaternion q1 = UNIT_QUATERNION;
-    axisAngleQ(&q1, CV3(2.0f, 5.0f, 7.0f), degreeToRadian(130.0f));
-    Quaternion q2 = UNIT_QUATERNION;
-    axisAngleQ(&q2, CV3(1.0f, 2.0f, 4.0f), degreeToRadian(260.0f));
-    Quaternion q3;
-    lerpQ(&q3, &q1, 0.3f, &q2);
-
-    char buffer[128];
-    toStringQ(&q3, buffer, sizeof(buffer));
-    TEST_STRING("(0.223 i + 0.529 j + 0.810 k + 0.119)", buffer);
+    Q q1(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(130.0f));
+    Q q2(V3(1.0f, 2.0f, 4.0f), Core::degreeToRadian(260.0f));
+    Q q3 = q1.lerp(0.3f, q2);
+    TEST_STRING("(0.223 i + 0.529 j + 0.810 k + 0.119)", q3);
 }
 
 static void testMulSet() {
-    Quaternion q1 = UNIT_QUATERNION;
-    axisAngleQ(&q1, CV3(2.0f, 5.0f, 7.0f), degreeToRadian(50.0f));
-    Quaternion q2 = UNIT_QUATERNION;
-    axisAngleQ(&q2, CV3(2.0f, 5.0f, 7.0f), degreeToRadian(60.0f));
-    Quaternion q3 = UNIT_QUATERNION;
-    mulSet(&q3, &q1);
-
-    char bufferQ1[128];
-    toStringQ(&q1, bufferQ1, sizeof(bufferQ1));
-    char bufferQ3[128];
-    toStringQ(&q3, bufferQ3, sizeof(bufferQ3));
-    TEST_STRING(bufferQ1, bufferQ3);
-
-    mulSet(&q3, &q2);
-    toStringQ(&q3, bufferQ3, sizeof(bufferQ3));
-
-    axisAngleQ(&q1, CV3(2.0f, 5.0f, 7.0f), degreeToRadian(110.0f));
-    toStringQ(&q1, bufferQ1, sizeof(bufferQ1));
-
-    TEST_STRING(bufferQ1, bufferQ3);
+    Q q1(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(50.0f));
+    Q q2(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(60.0f));
+    Q q3;
+    q3 *= q1;
+    TEST_STRING(q1, q3);
+    q3 *= q2;
+    TEST_STRING(Q(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(110.0f)), q3);
 }
 
 static void testMul() {
-    Quaternion q1 = UNIT_QUATERNION;
-    axisAngleQ(&q1, CV3(2.0f, 5.0f, 7.0f), degreeToRadian(50.0f));
-    Quaternion q2 = UNIT_QUATERNION;
-    axisAngleQ(&q2, CV3(2.0f, 5.0f, 7.0f), degreeToRadian(60.0f));
-    Quaternion q3 = UNIT_QUATERNION;
-    mul(&q3, &q1, &q2);
-
-    char bufferQ3[128];
-    toStringQ(&q3, bufferQ3, sizeof(bufferQ3));
-
-    Quaternion q = UNIT_QUATERNION;
-    axisAngleQ(&q, CV3(2.0f, 5.0f, 7.0f), degreeToRadian(110.0f));
-    char bufferQ[128];
-    toStringQ(&q, bufferQ, sizeof(bufferQ));
-
-    TEST_STRING(bufferQ, bufferQ3);
+    Q q1(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(50.0f));
+    Q q2(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(60.0f));
+    Q q3;
+    q3 = q1 * q2;
+    TEST_STRING(Q(V3(2.0f, 5.0f, 7.0f), Core::degreeToRadian(110.0f)), q3);
 }
 
 static void testMulVector() {
-    Quaternion q1 = UNIT_QUATERNION;
-    axisAngleQ(&q1, CV3(1.0f, 0.0f, 0.0f), degreeToRadian(90.0f));
-    Quaternion q2 = UNIT_QUATERNION;
-    axisAngleQ(&q2, CV3(0.0f, 1.0f, 0.0f), degreeToRadian(90.0f));
-    Quaternion q3 = UNIT_QUATERNION;
-    axisAngleQ(&q3, CV3(0.0f, 0.0f, 1.0f), degreeToRadian(90.0f));
-
-    Vector3 v1 = {{1.0f, 0.0f, 0.0f}};
-    Vector3 v2 = {{0.0f, 1.0f, 0.0f}};
-    Vector3 v3 = {{0.0f, 0.0f, 1.0f}};
-
-    TEST_V3(CV3(1.0f, 0.0f, 0.0f), mul(CV30, &q1, &v1));
-    TEST_V3(CV3(0.0f, 0.0f, 1.0f), mul(CV30, &q1, &v2));
-    TEST_V3(CV3(0.0f, -1.0f, 0.0f), mul(CV30, &q1, &v3));
-    TEST_V3(CV3(0.0f, 0.0f, -1.0f), mul(CV30, &q2, &v1));
-    TEST_V3(CV3(0.0f, 1.0f, 0.0f), mul(CV30, &q2, &v2));
-    TEST_V3(CV3(1.0f, 0.0f, 0.0f), mul(CV30, &q2, &v3));
-    TEST_V3(CV3(0.0f, 1.0f, 0.0f), mul(CV30, &q3, &v1));
-    TEST_V3(CV3(-1.0f, 0.0f, 0.0f), mul(CV30, &q3, &v2));
-    TEST_V3(CV3(0.0f, 0.0f, 1.0f), mul(CV30, &q3, &v3));
+    Q q1(V3(1.0f, 0.0f, 0.0f), Core::degreeToRadian(90.0f));
+    Q q2(V3(0.0f, 1.0f, 0.0f), Core::degreeToRadian(90.0f));
+    Q q3(V3(0.0f, 0.0f, 1.0f), Core::degreeToRadian(90.0f));
+
+    V3 v1(1.0f, 0.0f, 0.0f);
+    V3 v2(0.0f, 1.0f, 0.0f);
+    V3 v3(0.0f, 0.0f, 1.0f);
+
+    TEST(V3(1.0f, 0.0f, 0.0f), q1 * v1);
+    TEST(V3(0.0f, 0.0f, 1.0f), q1 * v2);
+    TEST(V3(0.0f, -1.0f, 0.0f), q1 * v3);
+    TEST(V3(0.0f, 0.0f, -1.0f), q2 * v1);
+    TEST(V3(0.0f, 1.0f, 0.0f), q2 * v2);
+    TEST(V3(1.0f, 0.0f, 0.0f), q2 * v3);
+    TEST(V3(0.0f, 1.0f, 0.0f), q3 * v1);
+    TEST(V3(-1.0f, 0.0f, 0.0f), q3 * v2);
+    TEST(V3(0.0f, 0.0f, 1.0f), q3 * v3);
 }
 
 void testQuaternion() {