diff --git a/src/OpenSimCreator/UI/Experimental/TPS2DTab.cpp b/src/OpenSimCreator/UI/Experimental/TPS2DTab.cpp index 0b88b63d4b..0fdaaf44ad 100644 --- a/src/OpenSimCreator/UI/Experimental/TPS2DTab.cpp +++ b/src/OpenSimCreator/UI/Experimental/TPS2DTab.cpp @@ -292,7 +292,7 @@ namespace Mesh ApplyThinPlateWarpToMesh(const ThinPlateWarper2D& t, const Mesh& mesh) { Mesh rv = mesh; - rv.transform_vertices([&t](Vec3 v) { return Vec3{t.transform(v), v.z}; }); + rv.transform_vertices([&t](Vec3 v) { return Vec3{t.transform(Vec2{v}), v.z}; }); return rv; } } diff --git a/src/OpenSimCreator/UI/MeshImporter/MeshImporterTab.cpp b/src/OpenSimCreator/UI/MeshImporter/MeshImporterTab.cpp index f55b4ef510..069276a2c2 100644 --- a/src/OpenSimCreator/UI/MeshImporter/MeshImporterTab.cpp +++ b/src/OpenSimCreator/UI/MeshImporter/MeshImporterTab.cpp @@ -2128,7 +2128,7 @@ class osc::mi::MeshImporterTab::Impl final : public IMeshImporterUILayerHost { MIObject& el = m_Shared->updModelGraph().updByID(id); switch (m_Gizmo.operation()) { case ui::GizmoOperation::Rotate: - el.applyRotation(m_Shared->getModelGraph(), userManipulation->rotation, m_GizmoModelMatrix[3]); + el.applyRotation(m_Shared->getModelGraph(), userManipulation->rotation, Vec3{m_GizmoModelMatrix[3]}); break; case ui::GizmoOperation::Translate: { el.applyTranslation(m_Shared->getModelGraph(), userManipulation->position); diff --git a/src/OpenSimCreator/UI/Shared/ModelSelectionGizmo.cpp b/src/OpenSimCreator/UI/Shared/ModelSelectionGizmo.cpp index 30e975421c..4850faab59 100644 --- a/src/OpenSimCreator/UI/Shared/ModelSelectionGizmo.cpp +++ b/src/OpenSimCreator/UI/Shared/ModelSelectionGizmo.cpp @@ -348,7 +348,7 @@ namespace getUndoableModel(), pof, ToVec3(X.p()), - ToVec3(X.R().convertRotationToBodyFixedXYZ()) + ToEulerAngles(X.R()) ); } else { @@ -371,7 +371,7 @@ namespace getUndoableModel(), pof, ToVec3(X.p()), - ToVec3(X.R().convertRotationToBodyFixedXYZ()) + ToEulerAngles(X.R()) ); } } @@ -425,7 +425,7 @@ namespace getUndoableModel(), wrapObj, ToVec3(X.p() - M_w.p()), - ToVec3(X.R().convertRotationToBodyFixedXYZ()) + ToEulerAngles(X.R()) ); } }; @@ -476,7 +476,7 @@ namespace getUndoableModel(), contactGeom, ToVec3(X.p() - M_w.p()), - ToVec3(X.R().convertRotationToBodyFixedXYZ()) + ToEulerAngles(X.R()) ); } }; @@ -577,7 +577,7 @@ namespace getUndoableModel(), parentPOF, ToVec3(X.p()), - ToVec3(X.R().convertRotationToBodyFixedXYZ()) + ToEulerAngles(X.R()) ); } @@ -595,7 +595,7 @@ namespace getUndoableModel(), childPOF, ToVec3(M_cpof2.p()), - ToVec3(M_cpof2.R().convertRotationToBodyFixedXYZ()) + ToEulerAngles(M_cpof2.R()) ); } }; diff --git a/src/OpenSimCreator/Utils/SimTKHelpers.cpp b/src/OpenSimCreator/Utils/SimTKHelpers.cpp index c684f63804..0596bf1020 100644 --- a/src/OpenSimCreator/Utils/SimTKHelpers.cpp +++ b/src/OpenSimCreator/Utils/SimTKHelpers.cpp @@ -165,6 +165,11 @@ Quat osc::ToQuat(const SimTK::Rotation& r) }; } +EulerAngles osc::ToEulerAngles(const SimTK::Rotation& r) +{ + return EulerAngles{ToVec3(r.convertRotationToBodyFixedXYZ())}; +} + std::array osc::ToArray(const SimTK::Vec6& v) { return { diff --git a/src/OpenSimCreator/Utils/SimTKHelpers.h b/src/OpenSimCreator/Utils/SimTKHelpers.h index dfe9d41f1f..69b2d4165b 100644 --- a/src/OpenSimCreator/Utils/SimTKHelpers.h +++ b/src/OpenSimCreator/Utils/SimTKHelpers.h @@ -37,6 +37,7 @@ namespace osc Mat3 ToMat3(const SimTK::Mat33&); Mat4 mat4_cast(const SimTK::Rotation&); Quat ToQuat(const SimTK::Rotation&); + EulerAngles ToEulerAngles(const SimTK::Rotation&); std::array ToArray(const SimTK::Vec6&); Transform decompose_to_transform(const SimTK::Transform&); } diff --git a/src/oscar/CMakeLists.txt b/src/oscar/CMakeLists.txt index fbf6595699..3aa1c3a2bb 100644 --- a/src/oscar/CMakeLists.txt +++ b/src/oscar/CMakeLists.txt @@ -226,6 +226,7 @@ add_library(oscar STATIC Maths/RayCollision.h Maths/Rect.h Maths/RectFunctions.h + Maths/Scalar.h Maths/Sphere.h Maths/Tetrahedron.h Maths/Transform.h diff --git a/src/oscar/Graphics/Unorm8.h b/src/oscar/Graphics/Unorm8.h index 7534da90f4..33cc87ec04 100644 --- a/src/oscar/Graphics/Unorm8.h +++ b/src/oscar/Graphics/Unorm8.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -72,4 +74,11 @@ namespace osc uint8_t value_; }; + + // tag `Unorm8` as scalar-like, so that other parts of the codebase (e.g. + // vectors, matrices) accept it + template<> + struct IsScalar final { + static constexpr bool value = true; + }; } diff --git a/src/oscar/Maths.h b/src/oscar/Maths.h index 49b162a0e7..523159d96d 100644 --- a/src/oscar/Maths.h +++ b/src/oscar/Maths.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include diff --git a/src/oscar/Maths/Angle.h b/src/oscar/Maths/Angle.h index d1c5c2f77a..df97da4a79 100644 --- a/src/oscar/Maths/Angle.h +++ b/src/oscar/Maths/Angle.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -32,13 +34,13 @@ namespace osc value_{static_cast(value_)} {} - // implicitly constructs from an angle expressed in other units + // implicitly constructs from an angle with the same representation, but expressed in other units template constexpr Angle(const Angle& other) : value_{static_cast(other.count() * (Units2::radians_per_rep/Units::radians_per_rep))} {} - // explicitly constructs from an angle expressed of type `Rep2` expressed in other units + // explicitly constructs from an angle with representation `Rep2`, expressed in other units template Rep2, AngularUnitTraits Units2> explicit constexpr Angle(const Angle& other) : value_{static_cast(other.count() * (Units2::radians_per_rep/Units::radians_per_rep))} @@ -149,6 +151,13 @@ namespace osc { return o << angle.count() << ' ' << Units::unit_label; } + + // tag `Unorm8` as scalar-like, so that other parts of the codebase (e.g. + // vectors, matrices) accept it + template + struct IsScalar> final { + static constexpr bool value = true; + }; } // a specialization of `std::common_type` for `osc::Angle`s @@ -203,7 +212,7 @@ namespace osc namespace literals { constexpr Degrees operator""_deg(long double degrees) { return Degrees{degrees}; } - constexpr Degrees operator""_deg(unsigned long long int degrees) { return Degrees{degrees};} + constexpr Degrees operator""_deg(unsigned long long int degrees) { return Degrees{degrees}; } } // turns diff --git a/src/oscar/Maths/EulerAngles.h b/src/oscar/Maths/EulerAngles.h index 6c611148ca..e76f5a95e9 100644 --- a/src/oscar/Maths/EulerAngles.h +++ b/src/oscar/Maths/EulerAngles.h @@ -5,6 +5,11 @@ namespace osc { - // a sequence of X -> Y -> Z euler angles - using EulerAngles = Vec<3, Radians>; + // in OSC, Euler angles represent an intrinsic 3D rotation about + // the X, then Y, then Z axes + + template + using EulerAnglesIn = Vec<3, Units>; // useful for writing `EulerAnglesIn(vec)` + + using EulerAngles = EulerAnglesIn; } diff --git a/src/oscar/Maths/MathsImplementation.cpp b/src/oscar/Maths/MathsImplementation.cpp index 203b9778f4..daa94bdd1b 100644 --- a/src/oscar/Maths/MathsImplementation.cpp +++ b/src/oscar/Maths/MathsImplementation.cpp @@ -1158,8 +1158,8 @@ AABB osc::bounding_aabb_of(const Sphere& sphere) Line osc::transform_line(const Line& line, const Mat4& mat) { Line rv{}; - rv.direction = mat * Vec4{line.direction, 0.0f}; - rv.origin = mat * Vec4{line.origin, 1.0f}; + rv.direction = Vec3{mat * Vec4{line.direction, 0.0f}}; + rv.origin = Vec3{mat * Vec4{line.origin, 1.0f}}; return rv; } @@ -1369,8 +1369,7 @@ Vec3 osc::transform_point(const Mat4& mat, const Vec3& point) Quat osc::to_worldspace_rotation_quat(const EulerAngles& eulers) { - static_assert(std::is_same_v); - return normalize(Quat{Vec3{eulers.x.count(), eulers.y.count(), eulers.z.count()}}); + return normalize(Quat{eulers}); } void osc::apply_worldspace_rotation( diff --git a/src/oscar/Maths/Qua.h b/src/oscar/Maths/Qua.h index 1c2f04131d..3891f961ba 100644 --- a/src/oscar/Maths/Qua.h +++ b/src/oscar/Maths/Qua.h @@ -83,8 +83,9 @@ namespace osc *this = normalize(Qua::wxyz(real_part, t.x, t.y, t.z)); } - // constructs a `Qua` from euler angles (pitch, yaw, roll), in radians. - explicit Qua(const Vec<3, T>& euler_angles) + // constructs a `Qua` from Euler angles that are assumed to represent an + // intrinsic, step-by-step, rotation about X, Y, and then Z + explicit Qua(const EulerAngles& euler_angles) { Vec<3, T> c = cos(euler_angles * T(0.5)); Vec<3, T> s = sin(euler_angles * T(0.5)); @@ -95,11 +96,6 @@ namespace osc this->z = c.x * c.y * s.z - s.x * s.y * c.z; } - // constructs a `Qua` from euler angles - explicit Qua(const EulerAngles& euler_angles) : - Qua(Vec3{euler_angles.x.count(), euler_angles.y.count(), euler_angles.z.count()}) - {} - // constructs a `Qua` by decomposing an orthogonal matrix explicit Qua(const Mat<3, 3, T>& m) { diff --git a/src/oscar/Maths/Scalar.h b/src/oscar/Maths/Scalar.h new file mode 100644 index 0000000000..3c4401deab --- /dev/null +++ b/src/oscar/Maths/Scalar.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace osc +{ + // metaclass that has a `value` member equal to `true` if its type argument + // behaves like a scalar (which is checked when resolving overloads of matrices + // and vectors) + template + struct IsScalar final { + static constexpr bool value = false; + }; + + template + struct IsScalar final { + static constexpr bool value = true; + }; + + template + struct IsScalar final { + static constexpr bool value = true; + }; + + template + inline constexpr bool IsScalarV = IsScalar::value; + + template + concept Scalar = IsScalarV; + + template + concept ScalarOrBoolean = Scalar || std::same_as; +} diff --git a/src/oscar/Maths/TrigonometricFunctions.h b/src/oscar/Maths/TrigonometricFunctions.h index 7026135219..da15647a22 100644 --- a/src/oscar/Maths/TrigonometricFunctions.h +++ b/src/oscar/Maths/TrigonometricFunctions.h @@ -27,6 +27,12 @@ namespace osc return map(v, sin); } + template + Vec sin(const Vec>& v) + { + return map(v, sin); + } + template GenType cos(GenType v) { @@ -45,6 +51,12 @@ namespace osc return map(v, cos); } + template + Vec cos(const Vec>& v) + { + return map(v, cos); + } + template GenType tan(GenType v) { @@ -60,7 +72,13 @@ namespace osc template Vec tan(const Vec& v) { - return elementwise_map(v, tan); + return map(v, tan); + } + + template + Vec tan(const Vec>& v) + { + return map(v, tan); } template diff --git a/src/oscar/Maths/Vec.h b/src/oscar/Maths/Vec.h index ce46918426..0e4e0219f7 100644 --- a/src/oscar/Maths/Vec.h +++ b/src/oscar/Maths/Vec.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -13,10 +14,10 @@ namespace osc { // specialized by `Vec2`, `Vec3`, etc. - template + template struct Vec; - template + template std::ostream& operator<<(std::ostream& out, const Vec& vec) { out << "Vec" << L << '('; @@ -29,7 +30,7 @@ namespace osc return out; } - template + template std::string to_string(const Vec& vec) { std::stringstream ss; @@ -39,30 +40,30 @@ namespace osc // when handled as a tuple-like object, a `Vec` decomposes into its elements - template + template constexpr const T& get(const Vec& vec) { return vec[I]; } - template + template constexpr T& get(Vec& vec) { return vec[I]; } - template + template constexpr T&& get(Vec&& vec) { return std::move(vec[I]); } - template + template constexpr const T&& get(const Vec&& vec) { return std::move(vec[I]); } } -template +template struct std::tuple_size> { static inline constexpr size_t value = L; }; -template +template struct std::tuple_element> { using type = T; }; -template +template struct std::hash> final { size_t operator()(const osc::Vec& vec) const { diff --git a/src/oscar/Maths/Vec2.h b/src/oscar/Maths/Vec2.h index 275870dc21..83d5c15be3 100644 --- a/src/oscar/Maths/Vec2.h +++ b/src/oscar/Maths/Vec2.h @@ -1,13 +1,15 @@ #pragma once +#include #include +#include #include #include namespace osc { - template + template struct Vec<2, T> { using value_type = T; using element_type = T; @@ -21,40 +23,47 @@ namespace osc using const_iterator = const T*; constexpr Vec() = default; - constexpr explicit Vec(T scalar) : - x{scalar}, - y{scalar} + explicit constexpr Vec(T value) : + x{value}, + y{value} {} constexpr Vec(T x_, T y_) : x{x_}, y{y_} {} template + requires std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to)) constexpr Vec(A x_, B y_) : x{static_cast(x_)}, y{static_cast(y_)} {} - template + template + requires std::constructible_from + explicit (not std::convertible_to) constexpr Vec(const Vec<2, U>& v) : x{static_cast(v.x)}, y{static_cast(v.y)} {} - template - constexpr Vec(const Vec<3, U>& v) : + template + requires std::constructible_from + explicit constexpr Vec(const Vec<3, U>& v) : x{static_cast(v.x)}, y{static_cast(v.y)} {} - template - constexpr Vec(const Vec<4, U>& v) : + template + requires std::constructible_from + explicit constexpr Vec(const Vec<4, U>& v) : x{static_cast(v.x)}, y{static_cast(v.y)} {} - template + template + requires std::assignable_from constexpr Vec& operator=(const Vec<2, U>& v) { - this->x = static_cast(v.x); - this->y = static_cast(v.y); + this->x = v.x; + this->y = v.y; return *this; } @@ -70,71 +79,80 @@ namespace osc friend constexpr bool operator==(const Vec<2, T>&, const Vec<2, T>&) = default; - template + template + requires (not std::same_as) constexpr Vec<2, T>& operator+=(U scalar) { - this->x += static_cast(scalar); - this->y += static_cast(scalar); + this->x += scalar; + this->y += scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator+=(const Vec<2, U>& rhs) { - this->x += static_cast(rhs.x); - this->y += static_cast(rhs.y); + this->x += rhs.x; + this->y += rhs.y; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator-=(U scalar) { - this->x -= static_cast(scalar); - this->y -= static_cast(scalar); + this->x -= scalar; + this->y -= scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator-=(const Vec<2, U>& rhs) { - this->x -= static_cast(rhs.x); - this->y -= static_cast(rhs.y); + this->x -= rhs.x; + this->y -= rhs.y; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator*=(U scalar) { - this->x *= static_cast(scalar); - this->y *= static_cast(scalar); + this->x *= scalar; + this->y *= scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator*=(Vec<2, U> const& rhs) { - this->x *= static_cast(rhs.x); - this->y *= static_cast(rhs.y); + this->x *= rhs.x; + this->y *= rhs.y; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator/=(U scalar) { - this->x /= static_cast(scalar); - this->y /= static_cast(scalar); + this->x /= scalar; + this->y /= scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator/=(const Vec<2, U>& rhs) { - this->x /= static_cast(rhs.x); - this->y /= static_cast(rhs.y); + this->x /= rhs.x; + this->y /= rhs.y; return *this; } constexpr Vec& operator++() + requires std::incrementable { ++this->x; ++this->y; @@ -142,6 +160,7 @@ namespace osc } constexpr Vec& operator--() + requires std::incrementable { --this->x; --this->y; @@ -149,6 +168,7 @@ namespace osc } constexpr Vec operator++(int) + requires std::incrementable { Vec copy{*this}; ++(*this); @@ -156,6 +176,7 @@ namespace osc } constexpr Vec operator--(int) + requires std::incrementable { Vec copy{*this}; --(*this); @@ -163,10 +184,11 @@ namespace osc } template - constexpr Vec with_element(size_type pos, U scalar) const + requires std::constructible_from + constexpr Vec with_element(size_type pos, U value) const { Vec copy{*this}; - copy[pos] = static_cast(scalar); + copy[pos] = static_cast(value); return copy; } @@ -174,98 +196,98 @@ namespace osc T y{}; }; - template + template constexpr Vec<2, T> operator+(const Vec<2, T>& vec) { - return vec; + return {+vec.x, +vec.y}; } - template + template constexpr Vec<2, T> operator-(const Vec<2, T>& vec) { - return Vec<2, T>{-vec.x, -vec.y}; + return {-vec.x, -vec.y}; } - template - constexpr Vec<2, T> operator+(const Vec<2, T>& vec, T scalar) + template + constexpr Vec<2, decltype(T{} + U{})> operator+(const Vec<2, T>& vec, U scalar) { - return Vec<2, T>{vec.x + scalar, vec.y + scalar}; + return {vec.x + scalar, vec.y + scalar}; } - template - constexpr Vec<2, T> operator+(T scalar, const Vec<2, T>& vec) + template + constexpr Vec<2, decltype(T{} + U{})> operator+(T scalar, const Vec<2, U>& vec) { - return Vec<2, T>{scalar + vec.x, scalar + vec.y}; + return {scalar + vec.x, scalar + vec.y}; } - template - constexpr Vec<2, T> operator+(const Vec<2, T>& lhs, const Vec<2, T>& rhs) + template + constexpr Vec<2, decltype(T{} + U{})> operator+(const Vec<2, T>& lhs, const Vec<2, U>& rhs) { - return Vec<2, T>{lhs.x + rhs.x, lhs.y + rhs.y}; + return {lhs.x + rhs.x, lhs.y + rhs.y}; } - template - constexpr Vec<2, T> operator-(const Vec<2, T>& vec, T scalar) + template + constexpr Vec<2, decltype(T{} - U{})> operator-(const Vec<2, T>& vec, U scalar) { - return Vec<2, T>{vec.x - scalar, vec.y - scalar}; + return {vec.x - scalar, vec.y - scalar}; } - template - constexpr Vec<2, T> operator-(T scalar, const Vec<2, T>& vec) + template + constexpr Vec<2, decltype(T{} - U{})> operator-(T scalar, const Vec<2, U>& vec) { - return Vec<2, T>{scalar - vec.x, scalar - vec.y}; + return {scalar - vec.x, scalar - vec.y}; } - template - constexpr Vec<2, T> operator-(const Vec<2, T>& lhs, const Vec<2, T>& rhs) + template + constexpr Vec<2, decltype(T{} - U{})> operator-(const Vec<2, T>& lhs, const Vec<2, U>& rhs) { - return Vec<2, T>{lhs.x - rhs.x, lhs.y - rhs.y}; + return {lhs.x - rhs.x, lhs.y - rhs.y}; } - template - constexpr Vec<2, T> operator*(const Vec<2, T>& vec, T scalar) + template + constexpr Vec<2, decltype(T{} * U{})> operator*(const Vec<2, T>& vec, U scalar) { - return Vec<2, T>{vec.x * scalar, vec.y * scalar}; + return {vec.x * scalar, vec.y * scalar}; } - template - constexpr Vec<2, T> operator*(T scalar, const Vec<2, T>& vec) + template + constexpr Vec<2, decltype(T{} * U{})> operator*(T scalar, const Vec<2, U>& vec) { - return Vec<2, T>{scalar * vec.x, scalar * vec.y}; + return {scalar * vec.x, scalar * vec.y}; } - template - constexpr Vec<2, T> operator*(const Vec<2, T>& lhs, const Vec<2, T>& rhs) + template + constexpr Vec<2, decltype(T{} * U{})> operator*(const Vec<2, T>& lhs, const Vec<2, U>& rhs) { - return Vec<2, T>{lhs.x * rhs.x, lhs.y * rhs.y}; + return {lhs.x * rhs.x, lhs.y * rhs.y}; } - template - constexpr Vec<2, T> operator/(const Vec<2, T>& vec, T scalar) + template + constexpr Vec<2, decltype(T{} / U{})> operator/(const Vec<2, T>& vec, U scalar) { - return Vec<2, T>{vec.x / scalar, vec.y / scalar}; + return {vec.x / scalar, vec.y / scalar}; } - template - constexpr Vec<2, T> operator/(T scalar, const Vec<2, T>& vec) + template + constexpr Vec<2, decltype(T{} / U{})> operator/(T scalar, const Vec<2, U>& vec) { - return Vec<2, T>{scalar / vec.x, scalar / vec.y}; + return {scalar / vec.x, scalar / vec.y}; } - template - constexpr Vec<2, T> operator/(const Vec<2, T>& lhs, const Vec<2, T>& rhs) + template + constexpr Vec<2, decltype(T{} / U{})> operator/(const Vec<2, T>& lhs, const Vec<2, U>& rhs) { - return Vec<2, T>{lhs.x / rhs.x, lhs.y / rhs.y}; + return {lhs.x / rhs.x, lhs.y / rhs.y}; } constexpr Vec<2, bool> operator&&(const Vec<2, bool>& lhs, const Vec<2, bool>& rhs) { - return Vec<2, bool>{lhs.x && rhs.x, lhs.y && rhs.y}; + return {lhs.x && rhs.x, lhs.y && rhs.y}; } constexpr Vec<2, bool> operator||(const Vec<2, bool>& lhs, const Vec<2, bool>& rhs) { - return Vec<2, bool>{lhs.x || rhs.x, lhs.y || rhs.y}; + return {lhs.x || rhs.x, lhs.y || rhs.y}; } using Vec2 = Vec<2, float>; diff --git a/src/oscar/Maths/Vec3.h b/src/oscar/Maths/Vec3.h index 159aa8b0d3..1689e294f3 100644 --- a/src/oscar/Maths/Vec3.h +++ b/src/oscar/Maths/Vec3.h @@ -1,13 +1,15 @@ #pragma once +#include #include +#include #include #include namespace osc { - template + template struct Vec<3, T> { using value_type = T; using element_type = T; @@ -21,10 +23,10 @@ namespace osc using const_iterator = const T*; constexpr Vec() = default; - constexpr explicit Vec(T scalar) : - x{scalar}, - y{scalar}, - z{scalar} + explicit constexpr Vec(T value) : + x{value}, + y{value}, + z{value} {} constexpr Vec(T x_, T y_, T z_) : x{x_}, @@ -32,42 +34,52 @@ namespace osc z{z_} {} template + requires std::constructible_from and std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to and std::convertible_to)) constexpr Vec(X x_, Y y_, Z z_) : x{static_cast(x_)}, y{static_cast(y_)}, z{static_cast(z_)} {} - template + template + requires std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to)) constexpr Vec(const Vec<2, A>& xy_, B z_) : x{static_cast(xy_.x)}, y{static_cast(xy_.y)}, z{static_cast(z_)} {} - template + template + requires std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to)) constexpr Vec(A x_, const Vec<2, B>& yz_) : x{static_cast(x_)}, y{static_cast(yz_.y)}, z{static_cast(yz_.z)} {} - template - constexpr Vec(const Vec<4, U>& v) : + template + requires std::constructible_from + explicit constexpr Vec(const Vec<4, U>& v) : x{static_cast(v.x)}, y{static_cast(v.y)}, z{static_cast(v.z)} {} - template + template + requires std::constructible_from + explicit (not (std::convertible_to)) constexpr Vec(const Vec<3, U>& v) : x{static_cast(v.x)}, y{static_cast(v.y)}, z{static_cast(v.z)} {} - template + template + requires std::assignable_from constexpr Vec& operator=(const Vec<3, U>& v) { - this->x = static_cast(v.x); - this->y = static_cast(v.y); - this->z = static_cast(v.z); + this->x = v.x; + this->y = v.y; + this->z = v.z; return *this; } @@ -83,79 +95,88 @@ namespace osc friend constexpr bool operator==(const Vec&, const Vec&) = default; - template + template + requires (not std::same_as) constexpr Vec& operator+=(U scalar) { - this->x += static_cast(scalar); - this->y += static_cast(scalar); - this->z += static_cast(scalar); + this->x += scalar; + this->y += scalar; + this->z += scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator+=(const Vec<3, U>& rhs) { - this->x += static_cast(rhs.x); - this->y += static_cast(rhs.y); - this->z += static_cast(rhs.z); + this->x += rhs.x; + this->y += rhs.y; + this->z += rhs.z; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator-=(U scalar) { - this->x -= static_cast(scalar); - this->y -= static_cast(scalar); - this->z -= static_cast(scalar); + this->x -= scalar; + this->y -= scalar; + this->z -= scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator-=(const Vec<3, U>& rhs) { - this->x -= static_cast(rhs.x); - this->y -= static_cast(rhs.y); - this->z -= static_cast(rhs.z); + this->x -= rhs.x; + this->y -= rhs.y; + this->z -= rhs.z; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator*=(U scalar) { - this->x *= static_cast(scalar); - this->y *= static_cast(scalar); - this->z *= static_cast(scalar); + this->x *= scalar; + this->y *= scalar; + this->z *= scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator*=(const Vec<3, U>& rhs) { - this->x *= static_cast(rhs.x); - this->y *= static_cast(rhs.y); - this->z *= static_cast(rhs.z); + this->x *= rhs.x; + this->y *= rhs.y; + this->z *= rhs.z; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator/=(U scalar) { - this->x /= static_cast(scalar); - this->y /= static_cast(scalar); - this->z /= static_cast(scalar); + this->x /= scalar; + this->y /= scalar; + this->z /= scalar; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator/=(const Vec<3, U>& rhs) { - this->x /= static_cast(rhs.x); - this->y /= static_cast(rhs.y); - this->z /= static_cast(rhs.z); + this->x /= rhs.x; + this->y /= rhs.y; + this->z /= rhs.z; return *this; } constexpr Vec& operator++() + requires std::incrementable { ++this->x; ++this->y; @@ -164,6 +185,7 @@ namespace osc } constexpr Vec& operator--() + requires std::incrementable { --this->x; --this->y; @@ -172,6 +194,7 @@ namespace osc } constexpr Vec operator++(int) + requires std::incrementable { Vec copy{*this}; ++(*this); @@ -179,6 +202,7 @@ namespace osc } constexpr Vec operator--(int) + requires std::incrementable { Vec copy{*this}; --(*this); @@ -186,10 +210,11 @@ namespace osc } template - constexpr Vec with_element(size_type pos, U scalar) const + requires std::constructible_from + constexpr Vec with_element(size_type pos, U value) const { Vec copy{*this}; - copy[pos] = static_cast(scalar); + copy[pos] = static_cast(value); return copy; } @@ -198,91 +223,91 @@ namespace osc T z{}; }; - template + template constexpr Vec<3, T> operator+(const Vec<3, T>& vec) { - return vec; + return {+vec.x, +vec.y, +vec.z}; } - template + template constexpr Vec<3, T> operator-(const Vec<3, T>& vec) { - return Vec<3, T>{-vec.x, -vec.y, -vec.z}; + return {-vec.x, -vec.y, -vec.z}; } - template - constexpr Vec<3, T> operator+(const Vec<3, T>& vec, T scalar) + template + constexpr Vec<3, decltype(T{} + U{})> operator+(const Vec<3, T>& vec, U scalar) { - return Vec<3, T>{vec.x + scalar, vec.y + scalar, vec.z + scalar}; + return {vec.x + scalar, vec.y + scalar, vec.z + scalar}; } - template - constexpr Vec<3, T> operator+(T scalar, const Vec<3, T>& vec) + template + constexpr Vec<3, decltype(T{} + U{})> operator+(T scalar, const Vec<3, U>& vec) { - return Vec<3, T>{scalar + vec.x, scalar + vec.y, scalar + vec.z}; + return {scalar + vec.x, scalar + vec.y, scalar + vec.z}; } - template - constexpr Vec<3, T> operator+(const Vec<3, T>& lhs, const Vec<3, T>& rhs) + template + constexpr Vec<3, decltype(T{} + U{})> operator+(const Vec<3, T>& lhs, const Vec<3, U>& rhs) { - return Vec<3, T>{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z}; + return {lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z}; } - template - constexpr Vec<3, T> operator-(const Vec<3, T>& vec, T scalar) + template + constexpr Vec<3, decltype(T{} - U{})> operator-(const Vec<3, T>& vec, U scalar) { - return Vec<3, T>{vec.x - scalar, vec.y - scalar, vec.z - scalar}; + return {vec.x - scalar, vec.y - scalar, vec.z - scalar}; } - template - constexpr Vec<3, T> operator-(T scalar, const Vec<3, T>& vec) + template + constexpr Vec<3, decltype(T{} - U{})> operator-(T scalar, const Vec<3, U>& vec) { - return Vec<3, T>{scalar - vec.x, scalar - vec.y, scalar - vec.z}; + return {scalar - vec.x, scalar - vec.y, scalar - vec.z}; } - template - constexpr Vec<3, T> operator-(const Vec<3, T>& lhs, const Vec<3, T>& rhs) + template + constexpr Vec<3, decltype(T{} - U{})> operator-(const Vec<3, T>& lhs, const Vec<3, U>& rhs) { - return Vec<3, T>{lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z}; + return {lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z}; } - template - constexpr Vec<3, T> operator*(const Vec<3, T>& vec, T scalar) + template + constexpr Vec<3, decltype(T{} * U{})> operator*(const Vec<3, T>& vec, U scalar) { - return Vec<3, T>{vec.x * scalar, vec.y * scalar, vec.z * scalar}; + return {vec.x * scalar, vec.y * scalar, vec.z * scalar}; } - template - constexpr Vec<3, T> operator*(T scalar, const Vec<3, T>& vec) + template + constexpr Vec<3, decltype(T{} * U{})> operator*(T scalar, const Vec<3, U>& vec) { - return Vec<3, T>{scalar * vec.x, scalar * vec.y, scalar * vec.z}; + return {scalar * vec.x, scalar * vec.y, scalar * vec.z}; } - template - constexpr Vec<3, T> operator*(const Vec<3, T>& lhs, const Vec<3, T>& rhs) + template + constexpr Vec<3, decltype(T{} * U{})> operator*(const Vec<3, T>& lhs, const Vec<3, U>& rhs) { - return Vec<3, T>{lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z}; + return {lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z}; } - template - constexpr Vec<3, T> operator/(const Vec<3, T>& vec, T scalar) + template + constexpr Vec<3, decltype(T{} / U{})> operator/(const Vec<3, T>& vec, U scalar) { - return Vec<3, T>{vec.x / scalar, vec.y / scalar, vec.z / scalar}; + return {vec.x / scalar, vec.y / scalar, vec.z / scalar}; } - template - constexpr Vec<3, T> operator/(T scalar, const Vec<3, T>& vec) + template + constexpr Vec<3, decltype(T{} / U{})> operator/(T scalar, const Vec<3, U>& vec) { - return Vec<3, T>{scalar / vec.x, scalar / vec.y, scalar / vec.z}; + return {scalar / vec.x, scalar / vec.y, scalar / vec.z}; } - template - constexpr Vec<3, T> operator/(const Vec<3, T>& lhs, const Vec<3, T>& rhs) + template + constexpr Vec<3, decltype(T{} / U{})> operator/(const Vec<3, T>& lhs, const Vec<3, U>& rhs) { - return Vec<3, T>{lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z}; + return {lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z}; } constexpr Vec<3, bool> operator&&(const Vec<3, bool>& lhs, const Vec<3, bool>& rhs) diff --git a/src/oscar/Maths/Vec4.h b/src/oscar/Maths/Vec4.h index b99858a490..0e8a185ef5 100644 --- a/src/oscar/Maths/Vec4.h +++ b/src/oscar/Maths/Vec4.h @@ -1,13 +1,15 @@ #pragma once +#include #include +#include #include #include namespace osc { - template + template struct Vec<4, T> { using value_type = T; using element_type = T; @@ -21,11 +23,11 @@ namespace osc using const_iterator = const T*; constexpr Vec() = default; - constexpr explicit Vec(T scalar) : - x{scalar}, - y{scalar}, - z{scalar}, - w{scalar} + constexpr explicit Vec(T value) : + x{value}, + y{value}, + z{value}, + w{value} {} constexpr Vec(T x_, T y_, T z_, T w_) : x{x_}, @@ -33,57 +35,72 @@ namespace osc z{z_}, w{w_} {} - template - constexpr Vec(X x_, Y y_, Z z_, W w_) : + template + requires std::constructible_from and std::constructible_from and std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to and std::convertible_to and std::convertible_to)) + constexpr Vec(A x_, B y_, C z_, D w_) : x{static_cast(x_)}, y{static_cast(y_)}, z{static_cast(z_)}, w{static_cast(w_)} {} - template + template + requires std::constructible_from and std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to and std::convertible_to)) constexpr Vec(const Vec<2, A>& xy_, B z_, C w_) : x{static_cast(xy_.x)}, y{static_cast(xy_.y)}, z{static_cast(z_)}, w{static_cast(w_)} {} - template + template + requires std::constructible_from and std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to and std::convertible_to)) constexpr Vec(A x_, const Vec<2, B>& yz_, C w_) : x{static_cast(x_)}, y{static_cast(yz_.x)}, z{static_cast(yz_.y)}, w{static_cast(w_)} {} - template + template + requires std::constructible_from and std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to and std::convertible_to)) constexpr Vec(A x_, B y_, const Vec<2, C>& zw_) : x{static_cast(x_)}, y{static_cast(y_)}, z{static_cast(zw_.x)}, w{static_cast(zw_.y)} {} - template + template + requires std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to)) constexpr Vec(const Vec<3, A>& xyz_, B w_) : x{static_cast(xyz_.x)}, y{static_cast(xyz_.y)}, z{static_cast(xyz_.z)}, w{static_cast(w_)} {} - template + template + requires std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to)) constexpr Vec(A x_, const Vec<3, B>& yzw_) : x{static_cast(x_)}, y{static_cast(yzw_.x)}, z{static_cast(yzw_.y)}, w{static_cast(yzw_.z)} {} - template + template + requires std::constructible_from and std::constructible_from + explicit (not (std::convertible_to and std::convertible_to)) constexpr Vec(const Vec<2, A>& xy_, const Vec<2, B>& wz_) : x{static_cast(xy_.x)}, y{static_cast(xy_.y)}, z{static_cast(wz_.x)}, w{static_cast(wz_.w)} {} - template - constexpr explicit Vec(const Vec<4, U>& v) : + template + requires std::constructible_from + explicit constexpr Vec(const Vec<4, A>& v) : x{static_cast(v.x)}, y{static_cast(v.y)}, z{static_cast(v.z)}, @@ -102,17 +119,19 @@ namespace osc constexpr friend bool operator==(const Vec&, const Vec&) = default; - template + template + requires std::assignable_from constexpr Vec& operator=(const Vec<4, U>& v) { - this->x = static_cast(v.x); - this->y = static_cast(v.y); - this->z = static_cast(v.z); - this->w = static_cast(v.w); + this->x = v.x; + this->y = v.y; + this->z = v.z; + this->w = v.w; return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator+=(U scalar) { this->x += static_cast(scalar); @@ -122,7 +141,8 @@ namespace osc return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator+=(const Vec<4, U>& rhs) { this->x += static_cast(rhs.x); @@ -132,7 +152,8 @@ namespace osc return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator-=(U scalar) { this->x -= static_cast(scalar); @@ -142,7 +163,8 @@ namespace osc return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator-=(const Vec<4, U>& rhs) { this->x -= static_cast(rhs.x); @@ -152,7 +174,8 @@ namespace osc return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator*=(U scalar) { this->x *= static_cast(scalar); @@ -162,7 +185,8 @@ namespace osc return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator*=(const Vec<4, U>& rhs) { this->x *= static_cast(rhs.x); @@ -172,7 +196,8 @@ namespace osc return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator/=(U scalar) { this->x /= static_cast(scalar); @@ -182,7 +207,8 @@ namespace osc return *this; } - template + template + requires (not std::same_as) constexpr Vec& operator/=(const Vec<4, U>& rhs) { this->x /= static_cast(rhs.x); @@ -193,6 +219,7 @@ namespace osc } constexpr Vec& operator++() + requires std::incrementable { ++this->x; ++this->y; @@ -202,6 +229,7 @@ namespace osc } constexpr Vec& operator--() + requires std::incrementable { --this->x; --this->y; @@ -211,6 +239,7 @@ namespace osc } constexpr Vec operator++(int) + requires std::incrementable { Vec copy{*this}; ++copy; @@ -218,6 +247,7 @@ namespace osc } constexpr Vec operator--(int) + requires std::incrementable { Vec copy{*this}; --copy; @@ -225,10 +255,11 @@ namespace osc } template - constexpr Vec with_element(size_type pos, U scalar) const + requires std::constructible_from + constexpr Vec with_element(size_type pos, U value) const { Vec copy{*this}; - copy[pos] = static_cast(scalar); + copy[pos] = static_cast(value); return copy; } @@ -238,89 +269,89 @@ namespace osc T w{}; }; - template + template constexpr Vec<4, T> operator+(const Vec<4, T>& vec) { - return vec; + return {+vec.x, +vec.y, +vec.z, +vec.w}; } - template + template constexpr Vec<4, T> operator-(const Vec<4, T>& vec) { - return Vec<4, T>{-vec.x, -vec.y, -vec.z, -vec.w}; + return {-vec.x, -vec.y, -vec.z, -vec.w}; } - template - constexpr Vec<4, T> operator+(const Vec<4, T>& vec, T scalar) + template + constexpr Vec<4, decltype(T{} + U{})> operator+(const Vec<4, T>& vec, U scalar) { - return Vec<4, T>{vec.x + scalar, vec.y + scalar, vec.z + scalar, vec.w + scalar}; + return {vec.x + scalar, vec.y + scalar, vec.z + scalar, vec.w + scalar}; } - template - constexpr Vec<4, T> operator+(T scalar, const Vec<4, T>& vec) + template + constexpr Vec<4, decltype(T{} + U{})> operator+(T scalar, const Vec<4, U>& vec) { - return Vec<4, T>{scalar + vec.x, scalar + vec.y, scalar + vec.z, scalar + vec.w}; + return {scalar + vec.x, scalar + vec.y, scalar + vec.z, scalar + vec.w}; } - template - constexpr Vec<4, T> operator+(const Vec<4, T>& lhs, const Vec<4, T>& rhs) + template + constexpr Vec<4, decltype(T{} + U{})> operator+(const Vec<4, T>& lhs, const Vec<4, U>& rhs) { - return Vec<4, T>{lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w}; + return {lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w}; } - template - constexpr Vec<4, T> operator-(const Vec<4, T>& vec, T scalar) + template + constexpr Vec<4, decltype(T{} - U{})> operator-(const Vec<4, T>& vec, U scalar) { - return Vec<4, T>{vec.x - scalar, vec.y - scalar, vec.z - scalar, vec.w - scalar}; + return {vec.x - scalar, vec.y - scalar, vec.z - scalar, vec.w - scalar}; } - template - constexpr Vec<4, T> operator-(T scalar, const Vec<4, T>& vec) + template + constexpr Vec<4, decltype(T{} - U{})> operator-(T scalar, const Vec<4, U>& vec) { - return Vec<4, T>{scalar - vec.x, scalar - vec.y, scalar - vec.z, scalar - vec.w}; + return {scalar - vec.x, scalar - vec.y, scalar - vec.z, scalar - vec.w}; } - template - constexpr Vec<4, T> operator-(const Vec<4, T>& lhs, const Vec<4, T>& rhs) + template + constexpr Vec<4, decltype(T{} - U{})> operator-(const Vec<4, T>& lhs, const Vec<4, U>& rhs) { - return Vec<4, T>{lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w}; + return {lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w}; } - template - constexpr Vec<4, T> operator*(const Vec<4, T>& vec, T scalar) + template + constexpr Vec<4, decltype(T{} * U{})> operator*(const Vec<4, T>& vec, U scalar) { - return Vec<4, T>{vec.x * scalar, vec.y * scalar, vec.z * scalar, vec.w * scalar}; + return {vec.x * scalar, vec.y * scalar, vec.z * scalar, vec.w * scalar}; } - template - constexpr Vec<4, T> operator*(T scalar, const Vec<4, T>& vec) + template + constexpr Vec<4, decltype(T{} * U{})> operator*(T scalar, const Vec<4, U>& vec) { - return Vec<4, T>{scalar * vec.x, scalar * vec.y, scalar * vec.z, scalar * vec.w}; + return {scalar * vec.x, scalar * vec.y, scalar * vec.z, scalar * vec.w}; } - template - constexpr Vec<4, T> operator*(const Vec<4, T>& lhs, const Vec<4, T>& rhs) + template + constexpr Vec<4, decltype(T{} * U{})> operator*(const Vec<4, T>& lhs, const Vec<4, U>& rhs) { - return Vec<4, T>{lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w}; + return {lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w}; } - template - constexpr Vec<4, T> operator/(const Vec<4, T>& vec, T scalar) + template + constexpr Vec<4, decltype(T{} / U{})> operator/(const Vec<4, T>& vec, U scalar) { - return Vec<4, T>{vec.x / scalar, vec.y / scalar, vec.z / scalar, vec.w / scalar}; + return {vec.x / scalar, vec.y / scalar, vec.z / scalar, vec.w / scalar}; } - template - constexpr Vec<4, T> operator/(T scalar, const Vec<4, T>& vec) + template + constexpr Vec<4, decltype(T{} / U{})> operator/(T scalar, const Vec<4, U>& vec) { - return Vec<4, T>{scalar / vec.x, scalar / vec.y, scalar / vec.z, scalar / vec.w}; + return {scalar / vec.x, scalar / vec.y, scalar / vec.z, scalar / vec.w}; } - template - constexpr Vec<4, T> operator/(const Vec<4, T>& lhs, const Vec<4, T>& rhs) + template + constexpr Vec<4, decltype(T{} / U{})> operator/(const Vec<4, T>& lhs, const Vec<4, U>& rhs) { - return Vec<4, T>{lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w}; + return {lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w}; } constexpr Vec<4, bool> operator&&(const Vec<4, bool>& lhs, const Vec<4, bool>& rhs) diff --git a/src/oscar/Maths/VecFunctions.h b/src/oscar/Maths/VecFunctions.h index da8808d089..6f90142820 100644 --- a/src/oscar/Maths/VecFunctions.h +++ b/src/oscar/Maths/VecFunctions.h @@ -1,18 +1,19 @@ #pragma once +#include #include #include namespace osc { - template + template constexpr const T* value_ptr(const Vec& vec) { return vec.data(); } - template + template constexpr T* value_ptr(Vec& vec) { return vec.data(); diff --git a/src/oscar/UI/oscimgui.cpp b/src/oscar/UI/oscimgui.cpp index d1e2d22a54..6b94f7f0f6 100644 --- a/src/oscar/UI/oscimgui.cpp +++ b/src/oscar/UI/oscimgui.cpp @@ -169,7 +169,7 @@ std::optional osc::ui::Gizmo::draw( style.ScaleLineCircleSize = 8.0f; } - const Vec3 original_translation = model_matrix[3]; + const Vec3 original_translation = Vec3{model_matrix[3]}; const bool gizmo_was_manipulated_by_user = ImGuizmo::Manipulate( value_ptr(view_matrix), value_ptr(projection_matrix), @@ -197,11 +197,10 @@ std::optional osc::ui::Gizmo::draw( value_ptr(world_rotation_in_degrees), value_ptr(world_scale) ); - const EulerAngles world_eulers = Vec<3, Degrees>(world_rotation_in_degrees); const GizmoTransform rv = { .scale = world_scale, - .rotation = world_eulers, + .rotation = EulerAnglesIn{world_rotation_in_degrees}, .position = world_translation, }; return rv; diff --git a/tests/testoscar/Maths/TestAngle.cpp b/tests/testoscar/Maths/TestAngle.cpp index 12ccabd1e2..5dce70c02d 100644 --- a/tests/testoscar/Maths/TestAngle.cpp +++ b/tests/testoscar/Maths/TestAngle.cpp @@ -112,3 +112,11 @@ TEST(Angle, CanUseProjectedClampWithAngles) }; static_assert(clamp(S{-10_deg}, S{0_deg}, S{180_deg}, {}, &S::ang).ang == S{0_deg}.ang); } + +TEST(Angle, std_is_convertible_to_works_between_angles) +{ + static_assert(std::convertible_to); + static_assert(std::convertible_to); + static_assert(std::convertible_to); + // etc. - many parts of the codebase require that these are automatically convertible +}