Fix mix-precision quaternion conversions (#7339)
This commit is contained in:
committed by
Benjamin Doherty
parent
0eb851ff8c
commit
cbc3bb3326
@@ -58,7 +58,7 @@ public:
|
||||
|
||||
/* compound assignment products by a scalar
|
||||
*/
|
||||
template<typename U>
|
||||
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
|
||||
constexpr QUATERNION<T>& operator*=(U v) {
|
||||
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
|
||||
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
|
||||
constexpr QUATERNION<T>& operator/=(U v) {
|
||||
QUATERNION<T>& lhs = static_cast<QUATERNION<T>&>(*this);
|
||||
for (size_t i = 0; i < QUATERNION<T>::size(); i++) {
|
||||
@@ -125,25 +125,25 @@ public:
|
||||
* q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w);
|
||||
*
|
||||
*/
|
||||
template<typename U>
|
||||
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
|
||||
friend inline constexpr
|
||||
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(QUATERNION<T> q, U scalar) {
|
||||
// don't pass q by reference because we need a copy anyway
|
||||
return q *= scalar;
|
||||
return QUATERNION<arithmetic_result_t<T, U>>(q *= scalar);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
|
||||
friend inline constexpr
|
||||
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(T scalar, QUATERNION<U> q) {
|
||||
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator*(U scalar, QUATERNION<T> q) {
|
||||
// don't pass q by reference because we need a copy anyway
|
||||
return q *= scalar;
|
||||
return QUATERNION<arithmetic_result_t<T, U>>(q *= scalar);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
template<typename U, typename = std::enable_if_t<std::is_arithmetic_v<U>>>
|
||||
friend inline constexpr
|
||||
QUATERNION<arithmetic_result_t<T, U>> MATH_PURE operator/(QUATERNION<T> q, U scalar) {
|
||||
// don't pass q by reference because we need a copy anyway
|
||||
return q /= scalar;
|
||||
return QUATERNION<arithmetic_result_t<T, U>>(q /= scalar);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <math.h>
|
||||
#include <random>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -324,3 +325,103 @@ TEST_F(QuatTest, NaN) {
|
||||
EXPECT_NEAR(qs[2], 0.5, 0.1);
|
||||
EXPECT_NEAR(qs[3], 0.5, 0.1);
|
||||
}
|
||||
|
||||
TEST_F(QuatTest, Conversions) {
|
||||
quat qd;
|
||||
quatf qf;
|
||||
float3 vf;
|
||||
double3 vd;
|
||||
double d = 0.0;
|
||||
float f = 0.0f;
|
||||
|
||||
static_assert(std::is_same<details::arithmetic_result_t<float, float>, float>::value);
|
||||
static_assert(std::is_same<details::arithmetic_result_t<float, double>, double>::value);
|
||||
static_assert(std::is_same<details::arithmetic_result_t<double, float>, double>::value);
|
||||
static_assert(std::is_same<details::arithmetic_result_t<double, double>, double>::value);
|
||||
|
||||
{
|
||||
auto r1 = qd * d;
|
||||
auto r2 = qd * f;
|
||||
auto r3 = qf * d;
|
||||
auto r4 = qf * f;
|
||||
static_assert(std::is_same<decltype(r1), quat>::value);
|
||||
static_assert(std::is_same<decltype(r2), quat>::value);
|
||||
static_assert(std::is_same<decltype(r3), quat>::value);
|
||||
static_assert(std::is_same<decltype(r4), quatf>::value);
|
||||
}
|
||||
{
|
||||
auto r1 = qd / d;
|
||||
auto r2 = qd / f;
|
||||
auto r3 = qf / d;
|
||||
auto r4 = qf / f;
|
||||
static_assert(std::is_same<decltype(r1), quat>::value);
|
||||
static_assert(std::is_same<decltype(r2), quat>::value);
|
||||
static_assert(std::is_same<decltype(r3), quat>::value);
|
||||
static_assert(std::is_same<decltype(r4), quatf>::value);
|
||||
}
|
||||
{
|
||||
auto r1 = d * qd;
|
||||
auto r2 = f * qd;
|
||||
auto r3 = d * qf;
|
||||
auto r4 = f * qf;
|
||||
static_assert(std::is_same<decltype(r1), quat>::value);
|
||||
static_assert(std::is_same<decltype(r2), quat>::value);
|
||||
static_assert(std::is_same<decltype(r3), quat>::value);
|
||||
static_assert(std::is_same<decltype(r4), quatf>::value);
|
||||
}
|
||||
{
|
||||
auto r1 = qd * vd;
|
||||
auto r2 = qf * vd;
|
||||
auto r3 = qd * vf;
|
||||
auto r4 = qf * vf;
|
||||
static_assert(std::is_same<decltype(r1), double3>::value);
|
||||
static_assert(std::is_same<decltype(r2), double3>::value);
|
||||
static_assert(std::is_same<decltype(r3), double3>::value);
|
||||
static_assert(std::is_same<decltype(r4), float3>::value);
|
||||
}
|
||||
{
|
||||
auto r1 = qd * qd;
|
||||
auto r2 = qf * qd;
|
||||
auto r3 = qd * qf;
|
||||
auto r4 = qf * qf;
|
||||
static_assert(std::is_same<decltype(r1), quat>::value);
|
||||
static_assert(std::is_same<decltype(r2), quat>::value);
|
||||
static_assert(std::is_same<decltype(r3), quat>::value);
|
||||
static_assert(std::is_same<decltype(r4), quatf>::value);
|
||||
}
|
||||
{
|
||||
auto r1 = dot(qd, qd);
|
||||
auto r2 = dot(qf, qd);
|
||||
auto r3 = dot(qd, qf);
|
||||
auto r4 = dot(qf, qf);
|
||||
static_assert(std::is_same<decltype(r1), double>::value);
|
||||
static_assert(std::is_same<decltype(r2), double>::value);
|
||||
static_assert(std::is_same<decltype(r3), double>::value);
|
||||
static_assert(std::is_same<decltype(r4), float>::value);
|
||||
}
|
||||
{
|
||||
auto r1 = cross(qd, qd);
|
||||
auto r2 = cross(qf, qd);
|
||||
auto r3 = cross(qd, qf);
|
||||
auto r4 = cross(qf, qf);
|
||||
static_assert(std::is_same<decltype(r1), quat>::value);
|
||||
static_assert(std::is_same<decltype(r2), quat>::value);
|
||||
static_assert(std::is_same<decltype(r3), quat>::value);
|
||||
static_assert(std::is_same<decltype(r4), quatf>::value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename L, typename R, typename = void>
|
||||
struct has_divide_assign : std::false_type {};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct has_divide_assign<L, R,
|
||||
decltype(std::declval<L&>() /= std::declval<R>(), void())> : std::true_type {};
|
||||
|
||||
// Static assertions to validate the availability of the /= operator for specific type
|
||||
// combinations. The first static_assert checks that the quat does not have a /= operator with Foo.
|
||||
// This ensures that quat does not provide an inappropriate overload that could be erroneously
|
||||
// selected.
|
||||
struct Foo {};
|
||||
static_assert(!has_divide_assign<quat, Foo>::value);
|
||||
static_assert(has_divide_assign<quat, float>::value);
|
||||
|
||||
Reference in New Issue
Block a user