blob: 4d93a0580afd68349dfbc470694bd03fadd74919 [file] [log] [blame]
/**
* Copyright (c) 2022 Alvin Wong
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <windowsnumerics.h>
#include <sstream>
using namespace Windows::Foundation::Numerics;
constexpr float pi = 3.141592654f;
std::ostream &operator<<(std::ostream &os, float2 const &v) {
os << "{" << v.x << ", " << v.y << "}";
return os;
}
std::ostream &operator<<(std::ostream &os, float3 const &v) {
os << "{" << v.x << ", " << v.y << ", " << v.z << "}";
return os;
}
std::ostream &operator<<(std::ostream &os, float4 const &v) {
os << "{" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << "}";
return os;
}
std::ostream &operator<<(std::ostream &os, float3x2 const &v) {
os << "{ {" << v.m11 << ", " << v.m12
<< "}, {" << v.m21 << ", " << v.m22
<< "}, {" << v.m31 << ", " << v.m32
<< "} }";
return os;
}
std::ostream &operator<<(std::ostream &os, float4x4 const &v) {
os << "{ {" << v.m11 << ", " << v.m12 << ", " << v.m13 << ", " << v.m14
<< "}, {" << v.m21 << ", " << v.m22 << ", " << v.m23 << ", " << v.m24
<< "}, {" << v.m31 << ", " << v.m32 << ", " << v.m33 << ", " << v.m34
<< "}, {" << v.m41 << ", " << v.m42 << ", " << v.m43 << ", " << v.m44
<< "} }";
return os;
}
std::ostream &operator<<(std::ostream &os, plane const &v) {
os << "{ {" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z
<< "}, " << v.d << "}";
return os;
}
std::ostream &operator<<(std::ostream &os, quaternion const &v) {
os << "{" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << "}";
return os;
}
#define CATCH_CONFIG_RUNNER
// https://github.com/catchorg/Catch2/releases/tag/v2.13.10
#include "catch.hpp"
class eq2
: public Catch::MatcherBase<float2>
{
float m_x{NAN};
float m_y{NAN};
public:
explicit eq2(float2 v)
: m_x(v.x), m_y(v.y)
{}
eq2(float x, float y)
: m_x(x), m_y(y)
{}
bool match(float2 const& v) const override {
return v.x == Approx(m_x) && v.y == Approx(m_y);
}
std::string describe() const override {
std::ostringstream ss;
ss << "is equal to ";
ss << float2(m_x, m_y);
return ss.str();
}
};
class eq3
: public Catch::MatcherBase<float3>
{
float m_x{NAN};
float m_y{NAN};
float m_z{NAN};
public:
explicit eq3(float3 v)
: m_x(v.x), m_y(v.y), m_z(v.z)
{}
eq3(float x, float y, float z)
: m_x(x), m_y(y), m_z(z)
{}
bool match(float3 const& v) const override {
return v.x == Approx(m_x) && v.y == Approx(m_y) && v.z == Approx(m_z);
}
std::string describe() const override {
std::ostringstream ss;
ss << "is equal to ";
ss << float3(m_x, m_y, m_z);
return ss.str();
}
};
class eq4
: public Catch::MatcherBase<float4>
{
float m_x{NAN};
float m_y{NAN};
float m_z{NAN};
float m_w{NAN};
public:
explicit eq4(float4 v)
: m_x(v.x), m_y(v.y), m_z(v.z), m_w(v.w)
{}
eq4(float x, float y, float z, float w)
: m_x(x), m_y(y), m_z(z), m_w(w)
{}
bool match(float4 const& v) const override {
return v.x == Approx(m_x) && v.y == Approx(m_y) && v.z == Approx(m_z) && v.w == Approx(m_w);
}
std::string describe() const override {
std::ostringstream ss;
ss << "is equal to ";
ss << float4(m_x, m_y, m_z, m_w);
return ss.str();
}
};
class eq3x2
: public Catch::MatcherBase<float3x2>
{
float m_11{NAN};
float m_12{NAN};
float m_21{NAN};
float m_22{NAN};
float m_31{NAN};
float m_32{NAN};
public:
explicit eq3x2(float3x2 v)
: m_11(v.m11), m_12(v.m12)
, m_21(v.m21), m_22(v.m22)
, m_31(v.m31), m_32(v.m32)
{}
eq3x2(
float m11, float m12,
float m21, float m22,
float m31, float m32
)
: m_11(m11), m_12(m12)
, m_21(m21), m_22(m22)
, m_31(m31), m_32(m32)
{}
bool match(float3x2 const& v) const override {
return
v.m11 == Approx(m_11) && v.m12 == Approx(m_12) &&
v.m21 == Approx(m_21) && v.m22 == Approx(m_22) &&
v.m31 == Approx(m_31) && v.m32 == Approx(m_32);
}
std::string describe() const override {
std::ostringstream ss;
ss << "is equal to ";
ss << float3x2(
m_11, m_12,
m_21, m_22,
m_31, m_32
);
return ss.str();
}
};
class eq4x4
: public Catch::MatcherBase<float4x4>
{
float m_11{NAN};
float m_12{NAN};
float m_13{NAN};
float m_14{NAN};
float m_21{NAN};
float m_22{NAN};
float m_23{NAN};
float m_24{NAN};
float m_31{NAN};
float m_32{NAN};
float m_33{NAN};
float m_34{NAN};
float m_41{NAN};
float m_42{NAN};
float m_43{NAN};
float m_44{NAN};
public:
explicit eq4x4(float4x4 v)
: m_11(v.m11), m_12(v.m12), m_13(v.m13), m_14(v.m14)
, m_21(v.m21), m_22(v.m22), m_23(v.m23), m_24(v.m24)
, m_31(v.m31), m_32(v.m32), m_33(v.m33), m_34(v.m34)
, m_41(v.m41), m_42(v.m42), m_43(v.m43), m_44(v.m44)
{}
eq4x4(
float m11, float m12, float m13, float m14,
float m21, float m22, float m23, float m24,
float m31, float m32, float m33, float m34,
float m41, float m42, float m43, float m44
)
: m_11(m11), m_12(m12), m_13(m13), m_14(m14)
, m_21(m21), m_22(m22), m_23(m23), m_24(m24)
, m_31(m31), m_32(m32), m_33(m33), m_34(m34)
, m_41(m41), m_42(m42), m_43(m43), m_44(m44)
{}
bool match(float4x4 const& v) const override {
return
v.m11 == Approx(m_11) && v.m12 == Approx(m_12) && v.m13 == Approx(m_13) && v.m14 == Approx(m_14) &&
v.m21 == Approx(m_21) && v.m22 == Approx(m_22) && v.m23 == Approx(m_23) && v.m24 == Approx(m_24) &&
v.m31 == Approx(m_31) && v.m32 == Approx(m_32) && v.m33 == Approx(m_33) && v.m34 == Approx(m_34) &&
v.m41 == Approx(m_41) && v.m42 == Approx(m_42) && v.m43 == Approx(m_43) && v.m44 == Approx(m_44);
}
std::string describe() const override {
std::ostringstream ss;
ss << "is equal to ";
ss << float4x4(
m_11, m_12, m_13, m_14,
m_21, m_22, m_23, m_24,
m_31, m_32, m_33, m_34,
m_41, m_42, m_43, m_44
);
return ss.str();
}
};
class eqp
: public Catch::MatcherBase<plane>
{
float m_x{NAN};
float m_y{NAN};
float m_z{NAN};
float m_d{NAN};
public:
explicit eqp(plane v)
: m_x(v.normal.x), m_y(v.normal.y), m_z(v.normal.z), m_d(v.d)
{}
eqp(float x, float y, float z, float d)
: m_x(x), m_y(y), m_z(z), m_d(d)
{}
bool match(plane const& v) const override {
return v.normal.x == Approx(m_x) && v.normal.y == Approx(m_y) &&
v.normal.z == Approx(m_z) && v.d == Approx(m_d);
}
std::string describe() const override {
std::ostringstream ss;
ss << "is equal to ";
ss << plane(m_x, m_y, m_z, m_d);
return ss.str();
}
};
class eqq
: public Catch::MatcherBase<quaternion>
{
float m_x{NAN};
float m_y{NAN};
float m_z{NAN};
float m_w{NAN};
public:
explicit eqq(quaternion v)
: m_x(v.x), m_y(v.y), m_z(v.z), m_w(v.w)
{}
eqq(float x, float y, float z, float w)
: m_x(x), m_y(y), m_z(z), m_w(w)
{}
bool match(quaternion const& v) const override {
return v.x == Approx(m_x) && v.y == Approx(m_y) && v.z == Approx(m_z) && v.w == Approx(m_w);
}
std::string describe() const override {
std::ostringstream ss;
ss << "is equal to ";
ss << quaternion(m_x, m_y, m_z, m_w);
return ss.str();
}
};
using namespace Catch::literals;
// === float2 ===
TEST_CASE("float2-ctors") {
CHECK_THAT(float2(2.0f, 3.0f), eq2(2.0f, 3.0f));
CHECK_THAT(float2(2.0f), eq2(2.0f, 2.0f));
}
TEST_CASE("float2-methods") {
CHECK_THAT(float2::zero(), eq2(0.0f, 0.0f));
CHECK(float2::zero().x == 0.0f);
CHECK(float2::zero().y == 0.0f);
CHECK_THAT(float2::one(), eq2(1.0f, 1.0f));
CHECK(float2::one().x == 1.0f);
CHECK(float2::one().y == 1.0f);
CHECK_THAT(float2::unit_x(), eq2(1.0f, 0.0f));
CHECK(float2::unit_x().x == 1.0f);
CHECK(float2::unit_x().y == 0.0f);
CHECK_THAT(float2::unit_y(), eq2(0.0f, 1.0f));
CHECK(float2::unit_y().x == 0.0f);
CHECK(float2::unit_y().y == 1.0f);
}
TEST_CASE("float2-operators") {
const float2 v1{ 3.0f, 4.0f };
const float2 v2{ 8.0f, 16.0f };
CHECK_THAT(v1 + v2, eq2(11.0f, 20.0f));
CHECK_THAT(v1 - v2, eq2(-5.0f, -12.0f));
CHECK_THAT(v1 * v2, eq2(24.0f, 64.0f));
CHECK_THAT(v1 * 2.0f, eq2(6.0f, 8.0f));
CHECK_THAT(3.0f * v1, eq2(9.0f, 12.0f));
CHECK_THAT(v1 / v2, eq2(0.375f, 0.25f));
CHECK_THAT(v1 / 10.0f, eq2(0.3f, 0.4f));
CHECK_THAT(-v1, eq2(-3.0f, -4.0f));
float2 v3;
v3 = {1.0f, 2.0f};
CHECK_THAT(v3 += v1, eq2(4.0f, 6.0f));
v3 = {1.0f, 2.0f};
CHECK_THAT(v3 -= v1, eq2(-2.0f, -2.0f));
v3 = {1.5f, 2.0f};
CHECK_THAT(v3 *= v1, eq2(4.5f, 8.0f));
v3 = {1.0f, 2.0f};
CHECK_THAT(v3 *= 3.0f, eq2(3.0f, 6.0f));
v3 = {1.0f, 2.0f};
CHECK_THAT(v3 /= v1, eq2(0.333333f, 0.5f));
v3 = {1.0f, 2.0f};
CHECK_THAT(v3 /= 5.0f, eq2(0.2f, 0.4f));
v3 = {3.0f, 4.0f};
CHECK((v3 == v1) == true);
CHECK((v3 == v2) == false);
CHECK((v3 != v1) == false);
CHECK((v3 != v2) == true);
}
TEST_CASE("float2-functions") {
const float2 v1{ 3.0f, 4.0f };
const float2 v2{ 8.0f, 16.0f };
const float2 v3{ 10.0f, 10.0f };
CHECK(length(v1) == 5.0_a);
CHECK(length(v2) == 17.88854_a);
CHECK(length_squared(v1) == 25.0_a);
CHECK(length_squared(v2) == 320.0_a);
CHECK(distance(v1, v2) == 13.0_a);
CHECK(distance_squared(v1, v2) == 169.0_a);
CHECK(dot(v1, v2) == 88.0_a);
CHECK_THAT(normalize(v1), eq2(0.6f, 0.8f));
CHECK_THAT(normalize(v2), eq2(0.4472136f, 0.8944272f));
CHECK_THAT(reflect(v1, float2::unit_x()), eq2(-3.0f, 4.0f));
CHECK_THAT(reflect(v1, float2::unit_y()), eq2(3.0f, -4.0f));
CHECK_THAT(reflect(v1, normalize(v2)), eq2(-1.4f, -4.8f));
CHECK_THAT(reflect(v1, v2), eq2(-1405, -2812)); // Appears to be garbage
CHECK_THAT(min(v2, v3), eq2(8.0f, 10.0f));
CHECK_THAT(max(v2, v3), eq2(10.0f, 16.0f));
CHECK_THAT(clamp(v1, v2, v3), eq2(8.0f, 16.0f));
CHECK_THAT(clamp(v2, v1, v3), eq2(8.0f, 10.0f));
CHECK_THAT(lerp(v1, v2, 0.0f), eq2(3.0f, 4.0f));
CHECK_THAT(lerp(v1, v2, 0.5f), eq2(5.5f, 10.0f));
CHECK_THAT(lerp(v1, v2, 1.0f), eq2(8.0f, 16.0f));
CHECK_THAT(lerp(v1, v2, -0.5f), eq2(0.5f, -2.0f));
CHECK_THAT(lerp(v1, v2, 1.5f), eq2(10.5f, 22.0f));
// TODO: transform
const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
CHECK_THAT(transform(v1, q1), eq2(3.06667f, -1.46667f));
}
// === float3 ===
TEST_CASE("float3-ctors") {
CHECK_THAT(float3(2.0f, 3.0f, 4.0f), eq3(2.0f, 3.0f, 4.0f));
CHECK_THAT(float3(float2(2.0f, 3.0f), 4.0f), eq3(2.0f, 3.0f, 4.0f));
CHECK_THAT(float3(2.0f), eq3(2.0f, 2.0f, 2.0f));
}
TEST_CASE("float3-methods") {
CHECK_THAT(float3::zero(), eq3(0.0f, 0.0f, 0.0f));
CHECK(float3::zero().x == 0.0f);
CHECK(float3::zero().y == 0.0f);
CHECK(float3::zero().z == 0.0f);
CHECK_THAT(float3::one(), eq3(1.0f, 1.0f, 1.0f));
CHECK(float3::one().x == 1.0f);
CHECK(float3::one().y == 1.0f);
CHECK(float3::one().z == 1.0f);
CHECK_THAT(float3::unit_x(), eq3(1.0f, 0.0f, 0.0f));
CHECK(float3::unit_x().x == 1.0f);
CHECK(float3::unit_x().y == 0.0f);
CHECK(float3::unit_x().z == 0.0f);
CHECK_THAT(float3::unit_y(), eq3(0.0f, 1.0f, 0.0f));
CHECK(float3::unit_y().x == 0.0f);
CHECK(float3::unit_y().y == 1.0f);
CHECK(float3::unit_y().z == 0.0f);
CHECK_THAT(float3::unit_z(), eq3(0.0f, 0.0f, 1.0f));
CHECK(float3::unit_z().x == 0.0f);
CHECK(float3::unit_z().y == 0.0f);
CHECK(float3::unit_z().z == 1.0f);
}
TEST_CASE("float3-operators") {
const float3 v1{ 3.0f, 4.0f, 5.0f };
const float3 v2{ 8.0f, 16.0f, 24.0f };
CHECK_THAT(v1 + v2, eq3(11.0f, 20.0f, 29.0f));
CHECK_THAT(v1 - v2, eq3(-5.0f, -12.0f, -19.0f));
CHECK_THAT(v1 * v2, eq3(24.0f, 64.0f, 120.0f));
CHECK_THAT(v1 * 2.0f, eq3(6.0f, 8.0f, 10.0f));
CHECK_THAT(3.0f * v1, eq3(9.0f, 12.0f, 15.0f));
CHECK_THAT(v1 / v2, eq3(0.375f, 0.25f, 0.208333f));
CHECK_THAT(v1 / 10.0f, eq3(0.3f, 0.4f, 0.5f));
CHECK_THAT(-v1, eq3(-3.0f, -4.0f, -5.0f));
float3 v3;
v3 = {1.0f, 2.0f, 3.0f};
CHECK_THAT(v3 += v1, eq3(4.0f, 6.0f, 8.0f));
v3 = {1.0f, 2.0f, 3.0f};
CHECK_THAT(v3 -= v1, eq3(-2.0f, -2.0f, -2.0f));
v3 = {1.5f, 2.0f, 2.5f};
CHECK_THAT(v3 *= v1, eq3(4.5f, 8.0f, 12.5f));
v3 = {1.0f, 2.0f, 3.0f};
CHECK_THAT(v3 *= 3.0f, eq3(3.0f, 6.0f, 9.0f));
v3 = {1.0f, 2.0f, 3.0f};
CHECK_THAT(v3 /= v1, eq3(0.333333f, 0.5f, 0.6f));
v3 = {1.0f, 2.0f, 3.0f};
CHECK_THAT(v3 /= 5.0f, eq3(0.2f, 0.4f, 0.6f));
v3 = {3.0f, 4.0f, 5.0f};
CHECK((v3 == v1) == true);
CHECK((v3 == v2) == false);
CHECK((v3 != v1) == false);
CHECK((v3 != v2) == true);
}
TEST_CASE("float3-functions") {
const float3 v1{ 3.0f, 4.0f, 5.0f };
const float3 v2{ 8.0f, 16.0f, 24.0f };
const float3 v3{ 10.0f, 10.0f, 10.0f };
CHECK(length(v1) == 7.07107_a);
CHECK(length(v2) == 29.93326_a);
CHECK(length_squared(v1) == 50.0_a);
CHECK(length_squared(v2) == 896.0_a);
CHECK(distance(v1, v2) == 23.02173_a);
CHECK(distance_squared(v1, v2) == 530.0_a);
CHECK(dot(v1, v2) == 208.0_a);
CHECK_THAT(normalize(v1), eq3(0.424264f, 0.565685f, 0.707107f));
CHECK_THAT(normalize(v2), eq3(0.267261f, 0.534522f, 0.801784f));
CHECK_THAT(cross(v1, v2), eq3(16.0f, -32.0f, 16.0f));
CHECK_THAT(cross(v2, v1), eq3(-16.0f, 32.0f, -16.0f));
CHECK_THAT(cross(v1, v3), eq3(-10.0f, 20.0f, -10.0f));
CHECK_THAT(cross(v2, v3), eq3(-80.0f, 160.0f, -80.0f));
CHECK_THAT(reflect(v1, float3::unit_x()), eq3(-3.0f, 4.0f, 5.0f));
CHECK_THAT(reflect(v1, float3::unit_y()), eq3(3.0f, -4.0f, 5.0f));
CHECK_THAT(reflect(v1, float3::unit_z()), eq3(3.0f, 4.0f, -5.0f));
CHECK_THAT(reflect(v1, normalize(v2)), eq3(-0.714285f, -3.42857f, -6.14286f));
CHECK_THAT(reflect(v1, v2), eq3(-3325, -6652, -9979)); // Appears to be garbage
CHECK_THAT(min(v2, v3), eq3(8.0f, 10.0f, 10.0f));
CHECK_THAT(max(v2, v3), eq3(10.0f, 16.0f, 24.0f));
CHECK_THAT(clamp(v1, v2, v3), eq3(8.0f, 16.0f, 24.0f));
CHECK_THAT(clamp(v2, v1, v3), eq3(8.0f, 10.0f, 10.0f));
CHECK_THAT(lerp(v1, v2, 0.0f), eq3(3.0f, 4.0f, 5.0f));
CHECK_THAT(lerp(v1, v2, 0.5f), eq3(5.5f, 10.0f, 14.5f));
CHECK_THAT(lerp(v1, v2, 1.0f), eq3(8.0f, 16.0f, 24.0f));
CHECK_THAT(lerp(v1, v2, -0.5f), eq3(0.5f, -2.0f, -4.5f));
CHECK_THAT(lerp(v1, v2, 1.5f), eq3(10.5f, 22.0f, 33.5f));
// TODO: transform
const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
CHECK_THAT(transform(v1, q1), eq3(6.73333f, -2.13333f, -0.333333f));
}
// === float4 ===
TEST_CASE("float4-ctors") {
CHECK_THAT(float4(2.0f, 3.0f, 4.0f, 5.0f), eq4(2.0f, 3.0f, 4.0f, 5.0f));
CHECK_THAT(float4(float2(2.0f, 3.0f), 4.0f, 5.0f), eq4(2.0f, 3.0f, 4.0f, 5.0f));
CHECK_THAT(float4(float3(2.0f, 3.0f, 4.0f), 5.0f), eq4(2.0f, 3.0f, 4.0f, 5.0f));
CHECK_THAT(float4(2.0f), eq4(2.0f, 2.0f, 2.0f, 2.0f));
}
TEST_CASE("float4-methods") {
CHECK_THAT(float4::zero(), eq4(0.0f, 0.0f, 0.0f, 0.0f));
CHECK(float4::zero().x == 0.0f);
CHECK(float4::zero().y == 0.0f);
CHECK(float4::zero().z == 0.0f);
CHECK(float4::zero().w == 0.0f);
CHECK_THAT(float4::one(), eq4(1.0f, 1.0f, 1.0f, 1.0f));
CHECK(float4::one().x == 1.0f);
CHECK(float4::one().y == 1.0f);
CHECK(float4::one().z == 1.0f);
CHECK(float4::one().w == 1.0f);
CHECK_THAT(float4::unit_x(), eq4(1.0f, 0.0f, 0.0f, 0.0f));
CHECK(float4::unit_x().x == 1.0f);
CHECK(float4::unit_x().y == 0.0f);
CHECK(float4::unit_x().z == 0.0f);
CHECK(float4::unit_x().w == 0.0f);
CHECK_THAT(float4::unit_y(), eq4(0.0f, 1.0f, 0.0f, 0.0f));
CHECK(float4::unit_y().x == 0.0f);
CHECK(float4::unit_y().y == 1.0f);
CHECK(float4::unit_y().z == 0.0f);
CHECK(float4::unit_y().w == 0.0f);
CHECK_THAT(float4::unit_z(), eq4(0.0f, 0.0f, 1.0f, 0.0f));
CHECK(float4::unit_z().x == 0.0f);
CHECK(float4::unit_z().y == 0.0f);
CHECK(float4::unit_z().z == 1.0f);
CHECK(float4::unit_z().w == 0.0f);
CHECK_THAT(float4::unit_w(), eq4(0.0f, 0.0f, 0.0f, 1.0f));
CHECK(float4::unit_w().x == 0.0f);
CHECK(float4::unit_w().y == 0.0f);
CHECK(float4::unit_w().z == 0.0f);
CHECK(float4::unit_w().w == 1.0f);
}
TEST_CASE("float4-operators") {
const float4 v1{ 3.0f, 4.0f, 5.0f, 6.0f };
const float4 v2{ 8.0f, 16.0f, 24.0f, 13.0f };
CHECK_THAT(v1 + v2, eq4(11.0f, 20.0f, 29.0f, 19.0f));
CHECK_THAT(v1 - v2, eq4(-5.0f, -12.0f, -19.0f, -7.0f));
CHECK_THAT(v1 * v2, eq4(24.0f, 64.0f, 120.0f, 78.0f));
CHECK_THAT(v1 * 2.0f, eq4(6.0f, 8.0f, 10.0f, 12.0f));
CHECK_THAT(3.0f * v1, eq4(9.0f, 12.0f, 15.0f, 18.0f));
CHECK_THAT(v1 / v2, eq4(0.375f, 0.25f, 0.208333f, 0.461538f));
CHECK_THAT(v1 / 10.0f, eq4(0.3f, 0.4f, 0.5f, 0.6f));
CHECK_THAT(-v1, eq4(-3.0f, -4.0f, -5.0f, -6.0f));
float4 v3;
v3 = {1.0f, 2.0f, 3.0f, 4.0f};
CHECK_THAT(v3 += v1, eq4(4.0f, 6.0f, 8.0f, 10.0f));
v3 = {1.0f, 2.0f, 3.0f, 4.0f};
CHECK_THAT(v3 -= v1, eq4(-2.0f, -2.0f, -2.0f, -2.0f));
v3 = {1.5f, 2.0f, 2.5f, 3.0f};
CHECK_THAT(v3 *= v1, eq4(4.5f, 8.0f, 12.5f, 18.0f));
v3 = {1.0f, 2.0f, 3.0f, 4.0f};
CHECK_THAT(v3 *= 3.0f, eq4(3.0f, 6.0f, 9.0f, 12.0f));
v3 = {1.0f, 2.0f, 3.0f, 4.0f};
CHECK_THAT(v3 /= v1, eq4(0.333333f, 0.5f, 0.6f, 0.666667f));
v3 = {1.0f, 2.0f, 3.0f, 4.0f};
CHECK_THAT(v3 /= 5.0f, eq4(0.2f, 0.4f, 0.6f, 0.8f));
v3 = {3.0f, 4.0f, 5.0f, 6.0f};
CHECK((v3 == v1) == true);
CHECK((v3 == v2) == false);
CHECK((v3 != v1) == false);
CHECK((v3 != v2) == true);
}
TEST_CASE("float4-functions") {
const float4 v1{ 3.0f, 4.0f, 5.0f, 6.0f };
const float4 v2{ 8.0f, 16.0f, 24.0f, 13.0f };
const float4 v3{ 10.0f, 10.0f, 10.0f, 10.0f };
CHECK(length(v1) == 9.27362_a);
CHECK(length(v2) == 32.63434_a);
CHECK(length_squared(v1) == 86.0_a);
CHECK(length_squared(v2) == 1065.0_a);
CHECK(distance(v1, v2) == 24.06242_a);
CHECK(distance_squared(v1, v2) == 579.0_a);
CHECK(dot(v1, v2) == 286.0_a);
CHECK_THAT(normalize(v1), eq4(0.323498f, 0.431331f, 0.539164f, 0.646997f));
CHECK_THAT(normalize(v2), eq4(0.245141f, 0.490281f, 0.735422f, 0.398353f));
CHECK_THAT(min(v2, v3), eq4(8.0f, 10.0f, 10.0f, 10.0f));
CHECK_THAT(max(v2, v3), eq4(10.0f, 16.0f, 24.0f, 13.0f));
CHECK_THAT(clamp(v1, v2, v3), eq4(8.0f, 16.0f, 24.0f, 13.0f));
CHECK_THAT(clamp(v2, v1, v3), eq4(8.0f, 10.0f, 10.0f, 10.0f));
CHECK_THAT(lerp(v1, v2, 0.0f), eq4(3.0f, 4.0f, 5.0f, 6.0f));
CHECK_THAT(lerp(v1, v2, 0.5f), eq4(5.5f, 10.0f, 14.5f, 9.5f));
CHECK_THAT(lerp(v1, v2, 1.0f), eq4(8.0f, 16.0f, 24.0f, 13.0f));
CHECK_THAT(lerp(v1, v2, -0.5f), eq4(0.5f, -2.0f, -4.5f, 2.5f));
CHECK_THAT(lerp(v1, v2, 1.5f), eq4(10.5f, 22.0f, 33.5f, 16.5f));
// TODO: transform
const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
const float3 v4{ 3.0f, 4.0f, 5.0f };
const float2 v5{ 3.0f, 4.0f };
CHECK_THAT(transform(v1, q1), eq4(6.73333f, -2.13333f, -0.333333f, 6.0f));
CHECK_THAT(transform4(v4, q1), eq4(6.73333f, -2.13333f, -0.333333f, 1.0f));
CHECK_THAT(transform4(v5, q1), eq4(3.06667f, -1.46667f, -3.66667f, 1.0f));
}
// === float3x2 ===
TEST_CASE("float3x2-ctors") {
CHECK_THAT(float3x2(
1.0f, 2.0f,
3.0f, 4.0f,
5.0f, 6.0f
), eq3x2(
1.0f, 2.0f,
3.0f, 4.0f,
5.0f, 6.0f
));
}
TEST_CASE("float3x2-methods") {
CHECK_THAT(float3x2::identity(), eq3x2(
1.0f, 0.0f,
0.0f, 1.0f,
0.0f, 0.0f
));
}
TEST_CASE("float3x2-operators") {
const float3x2 v1{
1.0f, 2.0f,
3.0f, 4.0f,
5.0f, 6.0f,
};
const float3x2 v2{
7.0f, 4.0f,
2.0f, 5.0f,
9.0f, 3.0f,
};
CHECK_THAT(v1 + v2, eq3x2(
8.0f, 6.0f,
5.0f, 9.0f,
14.0f, 9.0f
));
CHECK_THAT(v1 - v2, eq3x2(
-6.0f, -2.0f,
1.0f, -1.0f,
-4.0f, 3.0f
));
CHECK_THAT(v1 * v2, eq3x2(
// v1.m11 * v2.m11 + v1.m12 * v2.m21 + 0.0f * v2.m31, v1.m11 * v2.m12 + v1.m12 * v2.m22 + 0.0f * v2.m32,
// v1.m21 * v2.m11 + v1.m22 * v2.m21 + 0.0f * v2.m31, v1.m21 * v2.m12 + v1.m22 * v2.m22 + 0.0f * v2.m32,
// v1.m31 * v2.m11 + v1.m32 * v2.m21 + 1.0f * v2.m31, v1.m31 * v2.m12 + v1.m32 * v2.m22 + 1.0f * v2.m32
11.0f, 14.0f,
29.0f, 32.0f,
56.0f, 53.0f
));
CHECK_THAT(v1 * 2.0f, eq3x2(
2.0f, 4.0f,
6.0f, 8.0f,
10.0f, 12.0f
));
CHECK_THAT(-v1, eq3x2(
-1.0f, -2.0f,
-3.0f, -4.0f,
-5.0f, -6.0f
));
float3x2 v3;
v3 = {
9.0f, 3.0f,
8.0f, 2.0f,
7.0f, 1.0f,
};
CHECK_THAT(v3 += v1, eq3x2(
10.0f, 5.0f,
11.0f, 6.0f,
12.0f, 7.0f
));
v3 = {
9.0f, 3.0f,
8.0f, 2.0f,
7.0f, 1.0f,
};
CHECK_THAT(v3 -= v1, eq3x2(
8.0f, 1.0f,
5.0f, -2.0f,
2.0f, -5.0f
));
v3 = {
9.0f, 3.0f,
8.0f, 2.0f,
7.0f, 1.0f,
};
CHECK_THAT(v3 *= v1, eq3x2(
18.0f, 30.0f,
14.0f, 24.0f,
15.0f, 24.0f
));
v3 = {
9.0f, 3.0f,
8.0f, 2.0f,
7.0f, 1.0f,
};
CHECK_THAT(v3 *= 3.0f, eq3x2(
27.0f, 9.0f,
24.0f, 6.0f,
21.0f, 3.0f
));
v3 = {
1.0f, 2.0f,
3.0f, 4.0f,
5.0f, 6.0f,
};
CHECK((v3 == v1) == true);
CHECK((v3 == v2) == false);
CHECK((v3 != v1) == false);
CHECK((v3 != v2) == true);
}
TEST_CASE("float3x2-functions") {
const float3x2 v1{
1.0f, 2.0f,
3.0f, 4.0f,
5.0f, 6.0f,
};
CHECK(is_identity(v1) == false);
CHECK(is_identity(float3x2::identity()) == true);
CHECK(is_identity(float3x2{
1.0f, 0.0f,
0.0f, 1.0f,
0.0f, 0.0f,
}) == true);
CHECK(determinant(v1) == -2.0_a);
CHECK_THAT(translation(v1), eq2(5.0f, 6.0f));
// TODO
}
// === float4x4 ===
TEST_CASE("float4x4-ctors") {
CHECK_THAT(float4x4(
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f
), eq4x4(
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f
));
}
TEST_CASE("float4x4-methods") {
CHECK_THAT(float4x4::identity(), eq4x4(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
));
}
TEST_CASE("float4x4-operators") {
const float4x4 v1{
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f,
};
const float4x4 v2{
3.0f, 7.0f, 4.0f, 13.0f,
5.0f, 2.0f, 9.0f, 21.0f,
17.0f, 14.0f, 28.0f, 11.0f,
8.0f, 1.0f, 18.0f, 15.0f,
};
CHECK_THAT(v1 + v2, eq4x4(
4.0f, 9.0f, 7.0f, 17.0f,
10.0f, 8.0f, 16.0f, 29.0f,
26.0f, 24.0f, 39.0f, 23.0f,
21.0f, 15.0f, 33.0f, 31.0f
));
CHECK_THAT(v1 - v2, eq4x4(
-2.0f, -5.0f, -1.0f, -9.0f,
0.0f, 4.0f, -2.0f, -13.0f,
-8.0f, -4.0f, -17.0f, 1.0f,
5.0f, 13.0f, -3.0f, 1.0f
));
CHECK_THAT(v1 * v2, eq4x4(
96.0f, 57.0f, 178.0f, 148.0f,
228.0f, 153.0f, 414.0f, 388.0f,
360.0f, 249.0f, 650.0f, 628.0f,
492.0f, 345.0f, 886.0f, 868.0f
));
CHECK_THAT(v1 * 2.0f, eq4x4(
2.0f, 4.0f, 6.0f, 8.0f,
10.0f, 12.0f, 14.0f, 16.0f,
18.0f, 20.0f, 22.0f, 24.0f,
26.0f, 28.0f, 30.0f, 32.0f
));
CHECK_THAT(-v1, eq4x4(
-1.0f, -2.0f, -3.0f, -4.0f,
-5.0f, -6.0f, -7.0f, -8.0f,
-9.0f, -10.0f, -11.0f, -12.0f,
-13.0f, -14.0f, -15.0f, -16.0f
));
float4x4 v3;
v3 = {
5.0f, 2.0f, 9.0f, 21.0f,
3.0f, 7.0f, 4.0f, 13.0f,
8.0f, 1.0f, 18.0f, 15.0f,
17.0f, 14.0f, 28.0f, 11.0f,
};
CHECK_THAT(v3 += v1, eq4x4(
6.0f, 4.0f, 12.0f, 25.0f,
8.0f, 13.0f, 11.0f, 21.0f,
17.0f, 11.0f, 29.0f, 27.0f,
30.0f, 28.0f, 43.0f, 27.0f
));
v3 = {
5.0f, 2.0f, 9.0f, 21.0f,
3.0f, 7.0f, 4.0f, 13.0f,
8.0f, 1.0f, 18.0f, 15.0f,
17.0f, 14.0f, 28.0f, 11.0f,
};
CHECK_THAT(v3 -= v1, eq4x4(
4.0f, 0.0f, 6.0f, 17.0f,
-2.0f, 1.0f, -3.0f, 5.0f,
-1.0f, -9.0f, 7.0f, 3.0f,
4.0f, 0.0f, 13.0f, -5.0f
));
v3 = {
5.0f, 2.0f, 9.0f, 21.0f,
3.0f, 7.0f, 4.0f, 13.0f,
8.0f, 1.0f, 18.0f, 15.0f,
17.0f, 14.0f, 28.0f, 11.0f,
};
CHECK_THAT(v3 *= v1, eq4x4(
369.0f, 406.0f, 443.0f, 480.0f,
243.0f, 270.0f, 297.0f, 324.0f,
370.0f, 412.0f, 454.0f, 496.0f,
482.0f, 552.0f, 622.0f, 692.0f
));
v3 = {
5.0f, 2.0f, 9.0f, 21.0f,
3.0f, 7.0f, 4.0f, 13.0f,
8.0f, 1.0f, 18.0f, 15.0f,
17.0f, 14.0f, 28.0f, 11.0f,
};
CHECK_THAT(v3 *= 3.0f, eq4x4(
15.0f, 6.0f, 27.0f, 63.0f,
9.0f, 21.0f, 12.0f, 39.0f,
24.0f, 3.0f, 54.0f, 45.0f,
51.0f, 42.0f, 84.0f, 33.0f
));
v3 = {
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f,
};
CHECK((v3 == v1) == true);
CHECK((v3 == v2) == false);
CHECK((v3 != v1) == false);
CHECK((v3 != v2) == true);
}
TEST_CASE("float4x4-functions") {
const float4x4 v1{
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f,
};
const float4x4 v2{
7.0f, 5.0f, 2.4f, 0.0f,
4.0f, 1.3f, 5.9f, 0.0f,
8.2f, 3.4f, 1.0f, 0.0f,
4.7f, 5.1f, 2.2f, 1.0f,
};
CHECK(is_identity(v1) == false);
CHECK(is_identity(float4x4::identity()) == true);
CHECK(is_identity(float4x4{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
}) == true);
CHECK(determinant(v1) == 0.0_a);
CHECK(determinant(v2) == 97.636_a);
CHECK_THAT(translation(v2), eq3(4.7f, 5.1f, 2.2f));
CHECK_THAT(transpose(v1), eq4x4(
1.0f, 5.0f, 9.0f, 13.0f,
2.0f, 6.0f, 10.0f, 14.0f,
3.0f, 7.0f, 11.0f, 15.0f,
4.0f, 8.0f, 12.0f, 16.0f
));
CHECK_THAT(transpose(v2), eq4x4(
7.0f, 4.0f, 8.2f, 4.7f,
5.0f, 1.3f, 3.4f, 5.1f,
2.4f, 5.9f, 1.0f, 2.2f,
0.0f, 0.0f, 0.0f, 1.0f
));
// TODO
CHECK_THAT(make_float4x4_translation(float3(1.0f, 2.0f, 3.0f)), eq4x4(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 2.0f, 3.0f, 1.0f
));
CHECK_THAT(make_float4x4_scale(float3(4.0f, 5.0f, 6.0f)), eq4x4(
4.0f, 0.0f, 0.0f, 0.0f,
0.0f, 5.0f, 0.0f, 0.0f,
0.0f, 0.0f, 6.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
));
const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
CHECK_THAT(make_float4x4_from_quaternion(q1), eq4x4(
0.133333f, -0.933333f, -0.333333f, 0.0f,
0.666667f, 0.333333f, -0.666667f, 0.0f,
0.733333f, -0.133333f, 0.666667f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
));
CHECK_THAT(transform(v1, q1), eq4x4(
3.66667f, -0.666667f, 0.333333f, 4.0f,
9.8f, -3.6f, -0.999999f, 8.0f,
15.9333f, -6.53333f, -2.33333f, 12.0f,
22.0667f, -9.46667f, -3.66667f, 16.0f
));
}
// === plane ===
TEST_CASE("plane-ctors") {
CHECK_THAT(plane(1.0f, 2.0f, 3.0f, 4.0f), eqp(1.0f, 2.0f, 3.0f, 4.0f));
CHECK_THAT(plane(float3{1.0f, 2.0f, 3.0f}, 4.0f), eqp(1.0f, 2.0f, 3.0f, 4.0f));
CHECK_THAT(plane(float4{1.0f, 2.0f, 3.0f, 4.0f}), eqp(1.0f, 2.0f, 3.0f, 4.0f));
}
TEST_CASE("plane-operators") {
const plane v1 { 1.0f, 2.0f, 3.0f, 4.0f };
const plane v2 { 3.0f, 7.0f, 19.0f, 23.0f };
const plane v3 { 1.0f, 2.0f, 3.0f, 4.0f };
CHECK((v3 == v1) == true);
CHECK((v3 == v2) == false);
CHECK((v3 != v1) == false);
CHECK((v3 != v2) == true);
}
TEST_CASE("plane-functions") {
const plane v1 { 1.0f, 2.0f, 3.0f, 4.0f };
CHECK_THAT(normalize(v1), eqp(0.267261f, 0.534522f, 0.801784f, 1.06904f));
// TODO
const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
CHECK_THAT(transform(v1, q1), eqp(3.66667f, -0.666667f, 0.333333f, 4.0f));
}
// === quaternion ===
TEST_CASE("quaternion-ctors") {
CHECK_THAT(quaternion(1.0f, 2.0f, 3.0f, 4.0f), eqq(1.0f, 2.0f, 3.0f, 4.0f));
CHECK_THAT(quaternion(float3{1.0f, 2.0f, 3.0f}, 4.0f), eqq(1.0f, 2.0f, 3.0f, 4.0f));
}
TEST_CASE("quaternion-methods") {
CHECK_THAT(quaternion::identity(), eqq(0.0, 0.0, 0.0, 1.0));
}
TEST_CASE("quaternion-operators") {
const quaternion v1 { 1.0f, 2.0f, 3.0f, 4.0f };
const quaternion v2 { 3.0f, 7.0f, 19.0f, 23.0f };
CHECK_THAT(v1 + v2, eqq(4.0f, 9.0f, 22.0f, 27.0f));
CHECK_THAT(v1 - v2, eqq(-2.0f, -5.0f, -16.0f, -19.0f));
CHECK_THAT(v1 * v2, eqq(52.0f, 64.0f, 146.0f, 18.0f));
CHECK_THAT(v1 * 2.0f, eqq(2.0f, 4.0f, 6.0f, 8.0f));
CHECK_THAT(v1 / v2, eqq(-0.00632911f, 0.0295359f, -0.00843882f, 0.175105f));
CHECK_THAT(-v1, eqq(-1.0f, -2.0f, -3.0f, -4.0f));
quaternion v3;
v3 = {3.0f, 4.0f, 5.0f, 6.0f};
CHECK_THAT(v3 += v1, eqq(4.0f, 6.0f, 8.0f, 10.0f));
v3 = {3.0f, 4.0f, 5.0f, 6.0f};
CHECK_THAT(v3 -= v1, eqq(2.0f, 2.0f, 2.0f, 2.0f));
v3 = {1.5f, 2.0f, 2.5f, 3.0f};
CHECK_THAT(v3 *= v1, eqq(10.0f, 12.0f, 20.0f, -1.0f));
v3 = {3.0f, 4.0f, 5.0f, 6.0f};
CHECK_THAT(v3 *= 3.0f, eqq(9.0f, 12.0f, 15.0f, 18.0f));
v3 = {3.0f, 4.0f, 5.0f, 6.0f};
CHECK_THAT(v3 /= v1, eqq(0.133333f, 0.266667f, -5.96046e-8f, 1.66667f));
v3 = {1.0f, 2.0f, 3.0f, 4.0f};
CHECK((v3 == v1) == true);
CHECK((v3 == v2) == false);
CHECK((v3 != v1) == false);
CHECK((v3 != v2) == true);
}
TEST_CASE("quaternion-functions") {
const quaternion v1 { 1.0f, 2.0f, 3.0f, 4.0f };
CHECK(is_identity(v1) == false);
CHECK(is_identity(quaternion::identity()) == true);
CHECK(is_identity(quaternion{0.0f, 0.0f, 0.0f, 1.0f}) == true);
CHECK(length(v1) == 5.47723_a);
CHECK(length_squared(v1) == 30.0_a);
CHECK_THAT(normalize(v1), eqq(0.182574f, 0.365148f, 0.547723f, 0.730297f));
CHECK_THAT(conjugate(v1), eqq(-1.0f, -2.0f, -3.0f, 4.0f));
CHECK_THAT(inverse(v1), eqq(-0.0333333f, -0.0666667f, -0.1f, 0.133333f));
const float3 v2 { 0.0f, 1.0f, 0.0f };
CHECK_THAT(make_quaternion_from_axis_angle(v2, 0.0f), eqq(0.0f, 0.0f, 0.0f, 1.0f));
CHECK_THAT(make_quaternion_from_axis_angle(v2, -pi/2), eqq(0.0f, -0.707107f, 0.0f, 0.707107f));
CHECK_THAT(make_quaternion_from_yaw_pitch_roll(0.0f, 0.0f, 0.0f), eqq(0.0f, 0.0f, 0.0f, 1.0f));
CHECK_THAT(make_quaternion_from_yaw_pitch_roll(pi/2, 0.0f, 0.0f), eqq(0.0f, 0.707107f, 0.0f, 0.707107f));
CHECK_THAT(make_quaternion_from_yaw_pitch_roll(0.0f, pi/2, 0.0f), eqq(0.707107f, 0.0f, 0.0f, 0.707107f));
CHECK_THAT(make_quaternion_from_yaw_pitch_roll(0.0f, 0.0f, pi/2), eqq(0.0f, 0.0f, 0.707107f, 0.707107f));
CHECK_THAT(make_quaternion_from_yaw_pitch_roll(-pi/2, 0.0f, 0.0f), eqq(0.0f, -0.707107f, 0.0f, 0.707107f));
CHECK_THAT(make_quaternion_from_yaw_pitch_roll(pi/4, pi/4, pi/4), eqq(0.46194f, 0.191342f, 0.191342f, 0.844623f));
const float4x4 m1{
0.707107f, 0.0f, 0.707107f, 1.0f,
0.0f, 1.0f, 0.0f, 2.0f,
-0.707107f, 0.0f, 0.707107f, 3.0f,
4.0f, 5.0f, 6.0f, 1.0f,
};
CHECK_THAT(make_quaternion_from_rotation_matrix(m1), eqq(0.0f, -0.382684f, 0.0f, 0.92388f));
const float4x4 m2{
0.381126f, 0.904788f, 0.190003f, 0.0f,
0.523383f, -0.380566f, 0.762391f, 0.0f,
0.762111f, -0.191122f, -0.618594f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
CHECK_THAT(make_quaternion_from_rotation_matrix(m2), eqq(0.771409f, 0.462845f, 0.308563f, 0.309017f));
const float4x4 m3{
-0.618594f, 0.762391f, 0.190003f, 0.0f,
-0.191122f, -0.380566f, 0.904788f, 0.0f,
0.762111f, 0.523383f, 0.381126f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
CHECK_THAT(make_quaternion_from_rotation_matrix(m3), eqq(0.308563f, 0.462845f, 0.771409f, 0.309017f));
const float4x4 m4{
-0.618594f, 0.762111f, -0.191122f, 0.0f,
0.190003f, 0.381126f, 0.904788f, 0.0f,
0.762391f, 0.523383f, -0.380566f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
CHECK_THAT(make_quaternion_from_rotation_matrix(m4), eqq(0.308563f, 0.771409f, 0.462845f, 0.309017f));
const quaternion v3 { 4.0f, 3.0f, 2.0f, 1.0f };
CHECK(dot(v1, v3) == 20.0_a);
const quaternion v4 { 0.0f, 0.0f, 1.0f, 0.0f };
const quaternion v5 { 0.46194f, 0.191342f, 0.191342f, 0.844623f };
const quaternion v6 { -0.327447f, -0.218298f, -0.436596f, 0.809017f };
const quaternion v7 { 0.925506f, 0.154251f, 0.308502f, 0.156434f };
CHECK(dot(v4, v5) == 0.19134_a);
CHECK(dot(v6, v7) == -0.34486_a);
CHECK_THAT(slerp(v1, v3, 0.0), eqq(v1));
CHECK_THAT(slerp(v1, v3, 1.0), eqq(v3));
CHECK_THAT(slerp(v1, v3, 0.4), eqq(2.2f, 2.4f, 2.6f, 2.8f));
CHECK_THAT(slerp(v4, v5, 0.0), eqq(v4));
CHECK_THAT(slerp(v4, v5, 1.0), eqq(v5));
CHECK_THAT(slerp(v4, v5, 0.4), eqq(0.246519f, 0.102112f, 0.851841f, 0.450743f));
CHECK_THAT(slerp(v6, v7, 0.0), eqq(v6));
CHECK_THAT(slerp(v6, v7, 1.0), eqq(-v7));
CHECK_THAT(slerp(v6, v7, 0.4), eqq(-0.694796f, -0.232276f, -0.464552f, 0.497491f));
CHECK_THAT(lerp(v1, v3, 0.0), eqq(normalize(v1)));
CHECK_THAT(lerp(v1, v3, 1.0), eqq(normalize(v3)));
CHECK_THAT(lerp(v4, v5, 0.4), eqq(0.236225f, 0.0978475f, 0.86491f, 0.431919f));
CHECK_THAT(lerp(v4, v5, 0.0), eqq(v4));
CHECK_THAT(lerp(v4, v5, 1.0), eqq(v5));
CHECK_THAT(lerp(v4, v5, 0.4), eqq(0.236225f, 0.0978475f, 0.86491f, 0.431919f));
CHECK_THAT(lerp(v6, v7, 0.0), eqq(v6));
CHECK_THAT(lerp(v6, v7, 1.0), eqq(-v7));
CHECK_THAT(lerp(v6, v7, 0.4), eqq(-0.68441f, -0.232713f, -0.465426f, 0.510691f));
CHECK_THAT(concatenate(v1, v3), eqq(22.0f, 4.0f, 16.0f, -12.0f));
}
int main(int argc, char* argv[]) {
return Catch::Session().run(argc, argv);
}