diff --git a/headers/enduro2d/math/_all.hpp b/headers/enduro2d/math/_all.hpp index 38f38394..1c066384 100644 --- a/headers/enduro2d/math/_all.hpp +++ b/headers/enduro2d/math/_all.hpp @@ -13,6 +13,8 @@ #include "mat3.hpp" #include "mat4.hpp" #include "quat.hpp" +#include "ray2.hpp" +#include "ray3.hpp" #include "rect.hpp" #include "trig.hpp" #include "trs2.hpp" diff --git a/headers/enduro2d/math/_math.hpp b/headers/enduro2d/math/_math.hpp index a7ec8708..64e22016 100644 --- a/headers/enduro2d/math/_math.hpp +++ b/headers/enduro2d/math/_math.hpp @@ -31,6 +31,12 @@ namespace e2d template < typename T > class quat; + template < typename T > + class ray2; + + template < typename T > + class ray3; + template < typename T > class rect; @@ -101,6 +107,20 @@ namespace e2d using q4hi = quat; using q4hu = quat; + using r2d = ray2; + using r2f = ray2; + using r2i = ray2; + using r2u = ray2; + using r2hi = ray2; + using r2hu = ray2; + + using r3d = ray3; + using r3f = ray3; + using r3i = ray3; + using r3u = ray3; + using r3hi = ray3; + using r3hu = ray3; + using b2d = rect; using b2f = rect; using b2i = rect; diff --git a/headers/enduro2d/math/ray2.hpp b/headers/enduro2d/math/ray2.hpp new file mode 100644 index 00000000..1cc4ab7c --- /dev/null +++ b/headers/enduro2d/math/ray2.hpp @@ -0,0 +1,306 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "_math.hpp" +#include "vec2.hpp" + +namespace e2d +{ + template < typename T > + class ray2 final { + static_assert( + std::is_arithmetic_v, + "type of 'ray2' must be arithmetic"); + public: + using self_type = ray2; + using value_type = T; + public: + vec2 origin; + vec2 direction; + public: + static constexpr ray2 zero() noexcept; + static constexpr ray2 unit_x() noexcept; + static constexpr ray2 unit_y() noexcept; + public: + constexpr ray2() noexcept = default; + constexpr ray2(const ray2& other) noexcept = default; + constexpr ray2& operator=(const ray2& other) noexcept = default; + + constexpr ray2(T dx, T dy) noexcept; + constexpr ray2(T ox, T oy, T dx, T dy) noexcept; + + constexpr ray2(const vec2& direction) noexcept; + constexpr ray2(const vec2& origin, const vec2& direction) noexcept; + + template < typename To > + ray2 cast_to() const noexcept; + + T* data() noexcept; + const T* data() const noexcept; + + T& operator[](std::size_t index) noexcept; + T operator[](std::size_t index) const noexcept; + + ray2& operator+=(T v) noexcept; + ray2& operator-=(T v) noexcept; + ray2& operator*=(T v) noexcept; + ray2& operator/=(T v) noexcept; + + ray2& operator+=(const vec2& v) noexcept; + ray2& operator-=(const vec2& v) noexcept; + ray2& operator*=(const vec2& v) noexcept; + ray2& operator/=(const vec2& v) noexcept; + }; +} + +namespace e2d +{ + template < typename T > + constexpr ray2 ray2::zero() noexcept { + return {vec2::zero(), vec2::zero()}; + } + + template < typename T > + constexpr ray2 ray2::unit_x() noexcept { + return {vec2::zero(), vec2::unit_x()}; + } + + template < typename T > + constexpr ray2 ray2::unit_y() noexcept { + return {vec2::zero(), vec2::unit_y()}; + } + + template < typename T > + constexpr ray2::ray2(T dx, T dy) noexcept + : direction(dx, dy) {} + + template < typename T > + constexpr ray2::ray2(T ox, T oy, T dx, T dy) noexcept + : origin(ox, oy) + , direction(dx, dy) {} + + template < typename T > + constexpr ray2::ray2(const vec2& direction) noexcept + : direction(direction) {} + + template < typename T > + constexpr ray2::ray2(const vec2& origin, const vec2& direction) noexcept + : origin(origin) + , direction(direction) {} + + template < typename T > + template < typename To > + ray2 ray2::cast_to() const noexcept { + return { + origin.template cast_to(), + direction.template cast_to()}; + } + + template < typename T > + T* ray2::data() noexcept { + return origin.data(); + } + + template < typename T > + const T* ray2::data() const noexcept { + return origin.data(); + } + + template < typename T > + T& ray2::operator[](std::size_t index) noexcept { + E2D_ASSERT(index < 4); + return data()[index]; + } + + template < typename T > + T ray2::operator[](std::size_t index) const noexcept { + E2D_ASSERT(index < 4); + return data()[index]; + } + + template < typename T > + ray2& ray2::operator+=(T v) noexcept { + origin += v; + return *this; + } + + template < typename T > + ray2& ray2::operator-=(T v) noexcept { + origin -= v; + return *this; + } + + template < typename T > + ray2& ray2::operator*=(T v) noexcept { + direction *= v; + return *this; + } + + template < typename T > + ray2& ray2::operator/=(T v) noexcept { + direction /= v; + return *this; + } + + template < typename T > + ray2& ray2::operator+=(const vec2& v) noexcept { + origin += v; + return *this; + } + + template < typename T > + ray2& ray2::operator-=(const vec2& v) noexcept { + origin -= v; + return *this; + } + + template < typename T > + ray2& ray2::operator*=(const vec2& v) noexcept { + direction *= v; + return *this; + } + + template < typename T > + ray2& ray2::operator/=(const vec2& v) noexcept { + direction /= v; + return *this; + } +} + +namespace e2d +{ + // + // make_ray2 + // + + template < typename T > + constexpr ray2 make_ray2(T dx, T dy) noexcept { + return {dx, dy}; + } + + template < typename T > + constexpr ray2 make_ray2(T ox, T oy, T dx, T dy) noexcept { + return {ox, oy, dx, dy}; + } + + template < typename T > + constexpr ray2 make_ray2(const vec2& direction) noexcept { + return {direction}; + } + + template < typename T > + constexpr ray2 make_ray2(const vec2& origin, const vec2& direction) noexcept { + return {origin, direction}; + } + + // + // ray2 (==,!=) ray2 + // + + template < typename T > + bool operator==(const ray2& l, const ray2& r) noexcept { + return l.origin == r.origin + && l.direction == r.direction; + } + + template < typename T > + bool operator!=(const ray2& l, const ray2& r) noexcept { + return !(l == r); + } + + // + // ray2 (+,-,*,/) value + // + + template < typename T > + ray2 operator+(const ray2& l, T v) noexcept { + return { + l.origin + v, + l.direction}; + } + + template < typename T > + ray2 operator-(const ray2& l, T v) noexcept { + return { + l.origin - v, + l.direction}; + } + + template < typename T > + ray2 operator*(const ray2& l, T v) noexcept { + return { + l.origin, + l.direction * v}; + } + + template < typename T > + ray2 operator/(const ray2& l, T v) noexcept { + return { + l.origin, + l.direction / v}; + } + + // + // ray2 (+,-,*,/) vec2 + // + + template < typename T > + ray2 operator+(const ray2& l, const vec2& v) noexcept { + return { + l.origin + v, + l.direction}; + } + + template < typename T > + ray2 operator-(const ray2& l, const vec2& v) noexcept { + return { + l.origin - v, + l.direction}; + } + + template < typename T > + ray2 operator*(const ray2& l, const vec2& v) noexcept { + return { + l.origin, + l.direction * v}; + } + + template < typename T > + ray2 operator/(const ray2& l, const vec2& v) noexcept { + return { + l.origin, + l.direction / v}; + } +} + +namespace e2d::math +{ + // + // approximately + // + + template < typename T > + bool approximately( + const ray2& l, + const ray2& r, + T precision = math::default_precision()) noexcept + { + return math::approximately(l.origin, r.origin, precision) + && math::approximately(l.direction, r.direction, precision); + } + + // + // contains_nan + // + + template < typename T > + bool contains_nan(const ray2& r) noexcept { + return math::contains_nan(r.origin) + || math::contains_nan(r.direction); + } +} diff --git a/headers/enduro2d/math/ray3.hpp b/headers/enduro2d/math/ray3.hpp new file mode 100644 index 00000000..2684fc3e --- /dev/null +++ b/headers/enduro2d/math/ray3.hpp @@ -0,0 +1,312 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "_math.hpp" +#include "vec3.hpp" + +namespace e2d +{ + template < typename T > + class ray3 final { + static_assert( + std::is_arithmetic_v, + "type of 'ray3' must be arithmetic"); + public: + using self_type = ray3; + using value_type = T; + public: + vec3 origin; + vec3 direction; + public: + static constexpr ray3 zero() noexcept; + static constexpr ray3 unit_x() noexcept; + static constexpr ray3 unit_y() noexcept; + static constexpr ray3 unit_z() noexcept; + public: + constexpr ray3() noexcept = default; + constexpr ray3(const ray3& other) noexcept = default; + constexpr ray3& operator=(const ray3& other) noexcept = default; + + constexpr ray3(T dx, T dy, T dz) noexcept; + constexpr ray3(T ox, T oy, T oz, T dx, T dy, T dz) noexcept; + + constexpr ray3(const vec3& direction) noexcept; + constexpr ray3(const vec3& origin, const vec3& direction) noexcept; + + template < typename To > + ray3 cast_to() const noexcept; + + T* data() noexcept; + const T* data() const noexcept; + + T& operator[](std::size_t index) noexcept; + T operator[](std::size_t index) const noexcept; + + ray3& operator+=(T v) noexcept; + ray3& operator-=(T v) noexcept; + ray3& operator*=(T v) noexcept; + ray3& operator/=(T v) noexcept; + + ray3& operator+=(const vec3& v) noexcept; + ray3& operator-=(const vec3& v) noexcept; + ray3& operator*=(const vec3& v) noexcept; + ray3& operator/=(const vec3& v) noexcept; + }; +} + +namespace e2d +{ + template < typename T > + constexpr ray3 ray3::zero() noexcept { + return {vec3::zero(), vec3::zero()}; + } + + template < typename T > + constexpr ray3 ray3::unit_x() noexcept { + return {vec3::zero(), vec3::unit_x()}; + } + + template < typename T > + constexpr ray3 ray3::unit_y() noexcept { + return {vec3::zero(), vec3::unit_y()}; + } + + template < typename T > + constexpr ray3 ray3::unit_z() noexcept { + return {vec3::zero(), vec3::unit_z()}; + } + + template < typename T > + constexpr ray3::ray3(T dx, T dy, T dz) noexcept + : direction(dx, dy, dz) {} + + template < typename T > + constexpr ray3::ray3(T ox, T oy, T oz, T dx, T dy, T dz) noexcept + : origin(ox, oy, oz) + , direction(dx, dy, dz) {} + + template < typename T > + constexpr ray3::ray3(const vec3& direction) noexcept + : direction(direction) {} + + template < typename T > + constexpr ray3::ray3(const vec3& origin, const vec3& direction) noexcept + : origin(origin) + , direction(direction) {} + + template < typename T > + template < typename To > + ray3 ray3::cast_to() const noexcept { + return { + origin.template cast_to(), + direction.template cast_to()}; + } + + template < typename T > + T* ray3::data() noexcept { + return origin.data(); + } + + template < typename T > + const T* ray3::data() const noexcept { + return origin.data(); + } + + template < typename T > + T& ray3::operator[](std::size_t index) noexcept { + E2D_ASSERT(index < 6); + return data()[index]; + } + + template < typename T > + T ray3::operator[](std::size_t index) const noexcept { + E2D_ASSERT(index < 6); + return data()[index]; + } + + template < typename T > + ray3& ray3::operator+=(T v) noexcept { + origin += v; + return *this; + } + + template < typename T > + ray3& ray3::operator-=(T v) noexcept { + origin -= v; + return *this; + } + + template < typename T > + ray3& ray3::operator*=(T v) noexcept { + direction *= v; + return *this; + } + + template < typename T > + ray3& ray3::operator/=(T v) noexcept { + direction /= v; + return *this; + } + + template < typename T > + ray3& ray3::operator+=(const vec3& v) noexcept { + origin += v; + return *this; + } + + template < typename T > + ray3& ray3::operator-=(const vec3& v) noexcept { + origin -= v; + return *this; + } + + template < typename T > + ray3& ray3::operator*=(const vec3& v) noexcept { + direction *= v; + return *this; + } + + template < typename T > + ray3& ray3::operator/=(const vec3& v) noexcept { + direction /= v; + return *this; + } +} + +namespace e2d +{ + // + // make_ray3 + // + + template < typename T > + constexpr ray3 make_ray3(T dx, T dy, T dz) noexcept { + return {dx, dy, dz}; + } + + template < typename T > + constexpr ray3 make_ray3(T ox, T oy, T oz, T dx, T dy, T dz) noexcept { + return {ox, oy, oz, dx, dy, dz}; + } + + template < typename T > + constexpr ray3 make_ray3(const vec3& direction) noexcept { + return {direction}; + } + + template < typename T > + constexpr ray3 make_ray3(const vec3& origin, const vec3& direction) noexcept { + return {origin, direction}; + } + + // + // ray3 (==,!=) ray3 + // + + template < typename T > + bool operator==(const ray3& l, const ray3& r) noexcept { + return l.origin == r.origin + && l.direction == r.direction; + } + + template < typename T > + bool operator!=(const ray3& l, const ray3& r) noexcept { + return !(l == r); + } + + // + // ray3 (+,-,*,/) value + // + + template < typename T > + ray3 operator+(const ray3& l, T v) noexcept { + return { + l.origin + v, + l.direction}; + } + + template < typename T > + ray3 operator-(const ray3& l, T v) noexcept { + return { + l.origin - v, + l.direction}; + } + + template < typename T > + ray3 operator*(const ray3& l, T v) noexcept { + return { + l.origin, + l.direction * v}; + } + + template < typename T > + ray3 operator/(const ray3& l, T v) noexcept { + return { + l.origin, + l.direction / v}; + } + + // + // ray3 (+,-,*,/) vec3 + // + + template < typename T > + ray3 operator+(const ray3& l, const vec3& v) noexcept { + return { + l.origin + v, + l.direction}; + } + + template < typename T > + ray3 operator-(const ray3& l, const vec3& v) noexcept { + return { + l.origin - v, + l.direction}; + } + + template < typename T > + ray3 operator*(const ray3& l, const vec3& v) noexcept { + return { + l.origin, + l.direction * v}; + } + + template < typename T > + ray3 operator/(const ray3& l, const vec3& v) noexcept { + return { + l.origin, + l.direction / v}; + } +} + +namespace e2d::math +{ + // + // approximately + // + + template < typename T > + bool approximately( + const ray3& l, + const ray3& r, + T precision = math::default_precision()) noexcept + { + return math::approximately(l.origin, r.origin, precision) + && math::approximately(l.direction, r.direction, precision); + } + + // + // contains_nan + // + + template < typename T > + bool contains_nan(const ray3& r) noexcept { + return math::contains_nan(r.origin) + || math::contains_nan(r.direction); + } +} diff --git a/untests/sources/untests_math/ray2.cpp b/untests/sources/untests_math/ray2.cpp new file mode 100644 index 00000000..e11c495b --- /dev/null +++ b/untests/sources/untests_math/ray2.cpp @@ -0,0 +1,146 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "_math.hpp" +using namespace e2d; + +TEST_CASE("ray2") { + { + REQUIRE(r2i::zero() == r2i(v2i::zero())); + REQUIRE(r2i::unit_x() == r2i(v2i::unit_x())); + REQUIRE(r2i::unit_y() == r2i(v2i::unit_y())); + } + { + static_assert(std::is_same< + ray2, r2i::self_type + >::value, "static unit test error"); + static_assert(std::is_same< + i32, r2i::value_type + >::value, "static unit test error"); + static_assert(std::is_same< + vec2, decltype(std::declval().origin) + >::value, "static unit test error"); + static_assert(std::is_same< + vec2, decltype(std::declval().direction) + >::value, "static unit test error"); + + static_assert(std::is_same< + ray2, r2f::self_type + >::value, "static unit test error"); + static_assert(std::is_same< + f32, r2f::value_type + >::value, "static unit test error"); + static_assert(std::is_same< + vec2, decltype(std::declval().origin) + >::value, "static unit test error"); + static_assert(std::is_same< + vec2, decltype(std::declval().direction) + >::value, "static unit test error"); + } + { + REQUIRE(r2i().origin == v2i(0,0)); + REQUIRE(r2i().direction == v2i(0,0)); + + REQUIRE(r2i(1,2).origin == v2i(0,0)); + REQUIRE(r2i(1,2).direction == v2i(1,2)); + + REQUIRE(r2i({1,2}).origin == v2i(0,0)); + REQUIRE(r2i({1,2}).direction == v2i(1,2)); + + REQUIRE(r2i(1,2,3,4).origin == v2i(1,2)); + REQUIRE(r2i(1,2,3,4).direction == v2i(3,4)); + + REQUIRE(r2i({1,2},{3,4}).origin == v2i(1,2)); + REQUIRE(r2i({1,2},{3,4}).direction == v2i(3,4)); + } + { + REQUIRE(r2f({1,2},{3,4}).cast_to() == r2i({1,2},{3,4})); + } + { + auto r0 = r2i({1,2},{3,4}); + REQUIRE(r0.data()[0] == 1); + REQUIRE(r0.data()[1] == 2); + REQUIRE(r0.data()[2] == 3); + REQUIRE(r0.data()[3] == 4); + r0.data()[0] = 2; + REQUIRE(r0 == r2i({2,2},{3,4})); + const auto& cr0 = r0; + REQUIRE(cr0.data()[0] == 2); + REQUIRE(cr0.data()[1] == 2); + REQUIRE(cr0.data()[2] == 3); + REQUIRE(cr0.data()[3] == 4); + } + { + auto r0 = r2i({1,2},{3,4}); + REQUIRE(r0[0] == 1); + REQUIRE(r0[1] == 2); + REQUIRE(r0[2] == 3); + REQUIRE(r0[3] == 4); + r0[0] = 2; + REQUIRE(r0 == r2i({2,2},{3,4})); + const auto& cr0 = r0; + REQUIRE(cr0[0] == 2); + REQUIRE(cr0[1] == 2); + REQUIRE(cr0[2] == 3); + REQUIRE(cr0[3] == 4); + } + { + auto r0 = r2i({1,2},{3,4}); + REQUIRE(&r0 == &(r0 += 1)); + REQUIRE(r0 == r2i({2,3},{3,4})); + REQUIRE(&r0 == &(r0 -= 4)); + REQUIRE(r0 == r2i({-2,-1},{3,4})); + REQUIRE(&r0 == &(r0 *= 2)); + REQUIRE(r0 == r2i({-2,-1},{6,8})); + REQUIRE(&r0 == &(r0 /= 3)); + REQUIRE(r0 == r2i({-2,-1},{2,2})); + + REQUIRE(r2i({1,2},{3,4}) + 2 == r2i({3,4},{3,4})); + REQUIRE(r2i({1,2},{3,4}) - 2 == r2i({-1,0},{3,4})); + REQUIRE(r2i({1,2},{3,4}) * 3 == r2i({1,2},{9,12})); + REQUIRE(r2i({1,2},{6,9}) / 3 == r2i({1,2},{2,3})); + + REQUIRE(r2i({1,2},{3,4}) + v2i(1,2) == r2i({2,4},{3,4})); + REQUIRE(r2i({1,2},{3,4}) - v2i(1,2) == r2i({0,0},{3,4})); + REQUIRE(r2i({1,2},{3,4}) * v2i(2,3) == r2i({1,2},{6,12})); + REQUIRE(r2i({1,2},{6,8}) / v2i(3,2) == r2i({1,2},{2,4})); + } + { + REQUIRE(make_ray2(2,1) == r2i(0,0,2,1)); + REQUIRE(make_ray2(4,3,2,1) == r2i({4,3},{2,1})); + + REQUIRE(make_ray2(v2i{2,1}) == r2i({0,0},{2,1})); + REQUIRE(make_ray2(v2i{4,3},v2i{2,1}) == r2i({4,3},{2,1})); + } + { + auto r0 = r2i({1,2},{3,4}); + auto r1 = r2i(r0); + REQUIRE(r1 == r2i({1,2},{3,4})); + r1 = r2i({4,3},{2,1}); + REQUIRE(r1 == r2i({4,3},{2,1})); + } + { + REQUIRE(r2i({1,2},{3,4}) == r2i({1,2},{3,4})); + REQUIRE_FALSE(r2i({1,2},{3,4}) == r2i({1,2},{4,3})); + REQUIRE_FALSE(r2i({1,2},{3,4}) == r2i({2,1},{3,4})); + REQUIRE_FALSE(r2i({1,2},{3,4}) == r2i({2,1},{4,3})); + + REQUIRE_FALSE(r2i({1,2},{3,4}) != r2i({1,2},{3,4})); + REQUIRE(r2i({1,2},{3,4}) != r2i({1,2},{4,3})); + REQUIRE(r2i({1,2},{3,4}) != r2i({2,1},{3,4})); + REQUIRE(r2i({1,2},{3,4}) != r2i({2,1},{4,3})); + } + { + REQUIRE(math::approximately(r2i({1,2},{3,4}), r2i({1,2},{4,5}), 1)); + REQUIRE_FALSE(math::approximately(r2i({1,2},{3,4}), r2i({1,2},{4,5}))); + } + { + REQUIRE_FALSE(math::contains_nan(r2i({1,2},{3,4}))); + REQUIRE_FALSE(math::contains_nan(r2f({1.f,2.f},{3.f,4.f}))); + REQUIRE(math::contains_nan(r2f({1.f,std::numeric_limits::quiet_NaN()}))); + REQUIRE(math::contains_nan(r2f({std::numeric_limits::infinity(), 1.f}))); + } +} diff --git a/untests/sources/untests_math/ray3.cpp b/untests/sources/untests_math/ray3.cpp new file mode 100644 index 00000000..e73c474a --- /dev/null +++ b/untests/sources/untests_math/ray3.cpp @@ -0,0 +1,155 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "_math.hpp" +using namespace e2d; + +TEST_CASE("ray3") { + { + REQUIRE(r3i::zero() == r3i(v3i::zero())); + REQUIRE(r3i::unit_x() == r3i(v3i::unit_x())); + REQUIRE(r3i::unit_y() == r3i(v3i::unit_y())); + REQUIRE(r3i::unit_z() == r3i(v3i::unit_z())); + } + { + static_assert(std::is_same< + ray3, r3i::self_type + >::value, "static unit test error"); + static_assert(std::is_same< + i32, r3i::value_type + >::value, "static unit test error"); + static_assert(std::is_same< + vec3, decltype(std::declval().origin) + >::value, "static unit test error"); + static_assert(std::is_same< + vec3, decltype(std::declval().direction) + >::value, "static unit test error"); + + static_assert(std::is_same< + ray3, r3f::self_type + >::value, "static unit test error"); + static_assert(std::is_same< + f32, r3f::value_type + >::value, "static unit test error"); + static_assert(std::is_same< + vec3, decltype(std::declval().origin) + >::value, "static unit test error"); + static_assert(std::is_same< + vec3, decltype(std::declval().direction) + >::value, "static unit test error"); + } + { + REQUIRE(r3i().origin == v3i(0,0,0)); + REQUIRE(r3i().direction == v3i(0,0,0)); + + REQUIRE(r3i(1,2,3).origin == v3i(0,0,0)); + REQUIRE(r3i(1,2,3).direction == v3i(1,2,3)); + + REQUIRE(r3i({1,2,3}).origin == v3i(0,0,0)); + REQUIRE(r3i({1,2,3}).direction == v3i(1,2,3)); + + REQUIRE(r3i(1,2,3,3,4,5).origin == v3i(1,2,3)); + REQUIRE(r3i(1,2,3,3,4,5).direction == v3i(3,4,5)); + + REQUIRE(r3i({1,2,3},{3,4,5}).origin == v3i(1,2,3)); + REQUIRE(r3i({1,2,3},{3,4,5}).direction == v3i(3,4,5)); + } + { + REQUIRE(r3f({1,2,3},{3,4,5}).cast_to() == r3i({1,2,3},{3,4,5})); + } + { + auto r0 = r3i({1,2,3},{4,5,6}); + REQUIRE(r0.data()[0] == 1); + REQUIRE(r0.data()[1] == 2); + REQUIRE(r0.data()[2] == 3); + REQUIRE(r0.data()[3] == 4); + REQUIRE(r0.data()[4] == 5); + REQUIRE(r0.data()[5] == 6); + r0.data()[0] = 2; + REQUIRE(r0 == r3i({2,2,3},{4,5,6})); + const auto& cr0 = r0; + REQUIRE(cr0.data()[0] == 2); + REQUIRE(cr0.data()[1] == 2); + REQUIRE(cr0.data()[2] == 3); + REQUIRE(cr0.data()[3] == 4); + REQUIRE(cr0.data()[4] == 5); + REQUIRE(cr0.data()[5] == 6); + } + { + auto r0 = r3i({1,2,3},{4,5,6}); + REQUIRE(r0[0] == 1); + REQUIRE(r0[1] == 2); + REQUIRE(r0[2] == 3); + REQUIRE(r0[3] == 4); + REQUIRE(r0[4] == 5); + REQUIRE(r0[5] == 6); + r0[0] = 2; + REQUIRE(r0 == r3i({2,2,3},{4,5,6})); + const auto& cr0 = r0; + REQUIRE(cr0[0] == 2); + REQUIRE(cr0[1] == 2); + REQUIRE(cr0[2] == 3); + REQUIRE(cr0[3] == 4); + REQUIRE(cr0[4] == 5); + REQUIRE(cr0[5] == 6); + } + { + auto r0 = r3i({1,2,3},{4,5,6}); + REQUIRE(&r0 == &(r0 += 1)); + REQUIRE(r0 == r3i({2,3,4},{4,5,6})); + REQUIRE(&r0 == &(r0 -= 4)); + REQUIRE(r0 == r3i({-2,-1,0},{4,5,6})); + REQUIRE(&r0 == &(r0 *= 2)); + REQUIRE(r0 == r3i({-2,-1,0},{8,10,12})); + REQUIRE(&r0 == &(r0 /= 3)); + REQUIRE(r0 == r3i({-2,-1,0},{2,3,4})); + + REQUIRE(r3i({1,2,3},{4,5,6}) + 2 == r3i({3,4,5},{4,5,6})); + REQUIRE(r3i({1,2,3},{4,5,6}) - 2 == r3i({-1,0,1},{4,5,6})); + REQUIRE(r3i({1,2,3},{4,5,6}) * 2 == r3i({1,2,3},{8,10,12})); + REQUIRE(r3i({1,2,3},{4,5,6}) / 2 == r3i({1,2,3},{2,2,3})); + + REQUIRE(r3i({1,2,3},{4,5,6}) + v3i(1,2,3) == r3i({2,4,6},{4,5,6})); + REQUIRE(r3i({1,2,3},{4,5,6}) - v3i(3,2,1) == r3i({-2,0,2},{4,5,6})); + REQUIRE(r3i({1,2,3},{4,5,6}) * v3i(2,3,4) == r3i({1,2,3},{8,15,24})); + REQUIRE(r3i({1,2,3},{4,12,20}) / v3i(2,3,4) == r3i({1,2,3},{2,4,5})); + } + { + REQUIRE(make_ray3(2,1,0) == r3i(0,0,0,2,1,0)); + REQUIRE(make_ray3(4,3,2,2,1,0) == r3i({4,3,2},{2,1,0})); + + REQUIRE(make_ray3(v3i{2,1,0}) == r3i({0,0,0},{2,1,0})); + REQUIRE(make_ray3(v3i{4,3,2},v3i{2,1,0}) == r3i({4,3,2},{2,1,0})); + } + { + auto r0 = r3i({1,2,3},{4,5,6}); + auto r1 = r3i(r0); + REQUIRE(r1 == r3i({1,2,3},{4,5,6})); + r1 = r3i({4,3,2},{1,0,-1}); + REQUIRE(r1 == r3i({4,3,2},{1,0,-1})); + } + { + REQUIRE(r3i({1,2,3},{4,5,6}) == r3i({1,2,3},{4,5,6})); + REQUIRE_FALSE(r3i({1,2,3},{4,5,6}) == r3i({1,2,4},{3,5,6})); + REQUIRE_FALSE(r3i({1,2,3},{4,5,6}) == r3i({2,1,3},{4,5,6})); + REQUIRE_FALSE(r3i({1,2,3},{4,5,6}) == r3i({2,1,4},{3,5,6})); + + REQUIRE_FALSE(r3i({1,2,3},{4,5,6}) != r3i({1,2,3},{4,5,6})); + REQUIRE(r3i({1,2,3},{4,5,6}) != r3i({1,2,4},{3,5,6})); + REQUIRE(r3i({1,2,3},{4,5,6}) != r3i({2,1,3},{4,5,6})); + REQUIRE(r3i({1,2,3},{4,5,6}) != r3i({2,1,4},{3,5,6})); + } + { + REQUIRE(math::approximately(r3i({1,2,3},{4,1,2}), r3i({1,2,4},{5,1,2}), 1)); + REQUIRE_FALSE(math::approximately(r3i({1,2,3},{4,1,2}), r3i({1,2,4},{5,1,2}))); + } + { + REQUIRE_FALSE(math::contains_nan(r3i({1,2,3},{3,4,5}))); + REQUIRE_FALSE(math::contains_nan(r3f({1.f,2.f,3.f},{3.f,4.f,5.f}))); + REQUIRE(math::contains_nan(r3f({1.f,2.f,std::numeric_limits::quiet_NaN()}))); + REQUIRE(math::contains_nan(r3f({std::numeric_limits::infinity(), 1.f,2.f}))); + } +}