Also implement a couple make_float4x4_* functions that are useful for constructing input for some tests.
This reduces the number of unimplemented functions in this header a little bit. Signed-off-by: Martin Storsjö <mar...@martin.st> --- v2: Added an orthogonality check in decompose(), and check that no scale factors are zero (which would lead to divisions by zero). Rewrote slightly make_float4x4_from_quaternion based on a different reference. --- .../include/windowsnumerics.impl.h | 189 ++++++++++++++++-- .../testcases/test_windowsnumerics.cpp | 115 ++++++++++- 2 files changed, 283 insertions(+), 21 deletions(-) diff --git a/mingw-w64-headers/include/windowsnumerics.impl.h b/mingw-w64-headers/include/windowsnumerics.impl.h index 7785b1eba..76840b3a0 100644 --- a/mingw-w64-headers/include/windowsnumerics.impl.h +++ b/mingw-w64-headers/include/windowsnumerics.impl.h @@ -749,7 +749,11 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { inline float2 transform(const float2 &pos, const float4x4 &mat); inline float2 transform_normal(const float2 &norm, const float3x2 &mat); inline float2 transform_normal(const float2 &norm, const float4x4 &mat); - inline float2 transform(const float2 &val, const quaternion &rot); + inline float2 transform(const float2 &val, const quaternion &rot) { + // See comments in the float3 transform function. + quaternion result = rot * quaternion(float3(val.x, val.y, 0.0f), 0.0f) * inverse(rot); + return { result.x, result.y }; + } } _WINDOWS_NUMERICS_END_NAMESPACE_ @@ -801,7 +805,16 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { // TODO: impl inline float3 transform(const float3 &pos, const float4x4 &mat); inline float3 transform_normal(const float3 &norm, const float4x4 &mat); - inline float3 transform(const float3 &val, const quaternion &rot); + inline float3 transform(const float3 &val, const quaternion &rot) { + // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Using_quaternions_as_rotations + // If assuming rot is a unit quaternion, this could use + // conjugate() instead of inverse() too. + // This can be expressed as a matrix operation too, with + // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix + // (see make_float4x4_from_quaternion). + quaternion result = rot * quaternion(val, 0.0f) * inverse(rot); + return { result.x, result.y, result.z }; + } } _WINDOWS_NUMERICS_END_NAMESPACE_ @@ -863,9 +876,19 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { inline float4 transform(const float4 &pos, const float4x4 &mat); inline float4 transform4(const float3 &pos, const float4x4 &mat); inline float4 transform4(const float2 &pos, const float4x4 &mat); - inline float4 transform(const float4 &val, const quaternion &rot); - inline float4 transform4(const float3 &val, const quaternion &rot); - inline float4 transform4(const float2 &val, const quaternion &rot); + inline float4 transform(const float4 &val, const quaternion &rot) { + // See comments in the float3 transform function. + quaternion result = rot * quaternion(float3(val.x, val.y, val.z), 0.0f) * inverse(rot); + return { result.x, result.y, result.z, val.w }; + } + inline float4 transform4(const float3 &val, const quaternion &rot) { + quaternion result = rot * quaternion(val, 0.0f) * inverse(rot); + return { result.x, result.y, result.z, 1.0f }; + } + inline float4 transform4(const float2 &val, const quaternion &rot) { + quaternion result = rot * quaternion(float3(val.x, val.y, 0.0f), 0.0f) * inverse(rot); + return { result.x, result.y, result.z, 1.0f }; + } } _WINDOWS_NUMERICS_END_NAMESPACE_ @@ -910,11 +933,25 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { // TODO: impl inline float4x4 make_float4x4_billboard(const float3 &objpos, const float3 &camerapos, const float3 &cameraup, const float3 &camerafwd); inline float4x4 make_float4x4_constrained_billboard(const float3 &objpos, const float3 &camerapos, const float3 &rotateaxis, const float3 &camerafwd, const float3 &objfwd); - inline float4x4 make_float4x4_translation(const float3 &pos); + inline float4x4 make_float4x4_translation(const float3 &pos) { + return { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + pos.x, pos.y, pos.z, 1.0f + }; + } inline float4x4 make_float4x4_translation(float xpos, float ypos, float zpos); inline float4x4 make_float4x4_scale(float xscale, float yscale, float zscale); inline float4x4 make_float4x4_scale(float xscale, float yscale, float zscale, const float3 ¢er); - inline float4x4 make_float4x4_scale(const float3 &xyzscale); + inline float4x4 make_float4x4_scale(const float3 &xyzscale) { + return { + xyzscale.x, 0.0f, 0.0f, 0.0f, + 0.0f, xyzscale.y, 0.0f, 0.0f, + 0.0f, 0.0f, xyzscale.z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + } inline float4x4 make_float4x4_scale(const float3 &xyzscale, const float3 ¢er); inline float4x4 make_float4x4_scale(float scale); inline float4x4 make_float4x4_scale(float scale, const float3 ¢er); @@ -932,7 +969,24 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { inline float4x4 make_float4x4_orthographic_off_center(float left, float right, float bottom, float top, float znearplane, float zfarplane); inline float4x4 make_float4x4_look_at(const float3 &camerapos, const float3 &target, const float3 &cameraup); inline float4x4 make_float4x4_world(const float3 &pos, const float3 &fwd, const float3 &up); - inline float4x4 make_float4x4_from_quaternion(const quaternion &quat); + inline float4x4 make_float4x4_from_quaternion(const quaternion &quat) { + // https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion + float xx = quat.x * quat.x; + float yy = quat.y * quat.y; + float zz = quat.z * quat.z; + float xy = quat.x * quat.y; + float xz = quat.x * quat.z; + float xw = quat.x * quat.w; + float yz = quat.y * quat.z; + float yw = quat.y * quat.w; + float zw = quat.z * quat.w; + return { + 1.0f - 2.0f*yy - 2.0f*zz, 2.0f*xy + 2.0f*zw, 2.0f*xz - 2.0f*yw, 0.0f, + 2.0f*xy - 2.0f*zw, 1.0f - 2.0f*xx - 2.0f*zz, 2.0f*yz + 2.0f*xw, 0.0f, + 2.0f*xz + 2.0f*yw, 2.0f*yz - 2.0f*xw, 1.0f - 2.0f*xx - 2.0f*yy, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + } inline float4x4 make_float4x4_from_yaw_pitch_roll(float yaw, float pitch, float roll); inline float4x4 make_float4x4_shadow(const float3 &lightdir, const plane &plane); inline float4x4 make_float4x4_reflection(const plane &plane); @@ -955,8 +1009,37 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { return { val.m41, val.m42, val.m43 }; } inline bool invert(const float4x4 &mat, float4x4 *out); - inline bool decompose(const float4x4 &mat, float3 *out_scale, quaternion *out_rot, float3 *out_translate); - inline float4x4 transform(const float4x4 &val, const quaternion &rot); + inline bool decompose(const float4x4 &mat, float3 *out_scale, quaternion *out_rot, float3 *out_translate) { + float4x4 val = mat; + if (out_translate) + *out_translate = translation(val); + val.m41 = val.m42 = val.m43 = 0.0f; + float3 scale = float3( + length(float3(val.m11, val.m12, val.m13)), + length(float3(val.m21, val.m22, val.m23)), + length(float3(val.m31, val.m32, val.m33)) + ); + if (out_scale) + *out_scale = scale; + if (scale.x <= 0 || scale.y <= 0 || scale.z <= 0) + return false; + val = make_float4x4_scale(float3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z)) * val; + float3 v1 = { val.m11, val.m21, val.m31 }; + float3 v2 = { val.m12, val.m22, val.m32 }; + float3 v3 = { val.m13, val.m23, val.m33 }; + float d1 = dot(v1, v2); + float d2 = dot(v1, v3); + float d3 = dot(v2, v3); + const float epsilon = 1.0e-3f; + if (::std::fabsf(d1) > epsilon || ::std::fabsf(d2) > epsilon || ::std::fabsf(d3) > epsilon) + return false; + if (out_rot) + *out_rot = make_quaternion_from_rotation_matrix(val); + return true; + } + inline float4x4 transform(const float4x4 &val, const quaternion &rot) { + return val * make_float4x4_from_quaternion(rot); + } inline float4x4 transpose(const float4x4 &val) { return { val.m11, val.m21, val.m31, val.m41, @@ -980,7 +1063,10 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { return { val.normal * invlen, val.d * invlen }; } inline plane transform(const plane &plane, const float4x4 &mat); - inline plane transform(const plane &plane, const quaternion &rot); + inline plane transform(const plane &plane, const quaternion &rot) { + quaternion result = rot * quaternion(plane.normal, 0.0f) * inverse(rot); + return { float3(result.x, result.y, result.z), plane.d }; + } inline float dot(const plane &plane, const float4 &val); inline float dot_coordinate(const plane &plane, const float3 &val); inline float dot_normal(const plane &plane, const float3 &val); @@ -991,10 +1077,40 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { // Define quaternion functions _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { - // TODO: impl - inline quaternion make_quaternion_from_axis_angle(const float3 &axis, float angle); - inline quaternion make_quaternion_from_yaw_pitch_roll(float yaw, float pitch, float roll); - inline quaternion make_quaternion_from_rotation_matrix(const float4x4 &mat); + inline quaternion make_quaternion_from_axis_angle(const float3 &axis, float angle) { + return quaternion(axis * ::std::sin(angle * 0.5f), ::std::cos(angle * 0.5f)); + } + inline quaternion make_quaternion_from_yaw_pitch_roll(float yaw, float pitch, float roll) { + quaternion yq = make_quaternion_from_axis_angle(float3(0.0f, 1.0f, 0.0f), yaw); + quaternion pq = make_quaternion_from_axis_angle(float3(1.0f, 0.0f, 0.0f), pitch); + quaternion rq = make_quaternion_from_axis_angle(float3(0.0f, 0.0f, 1.0f), roll); + return concatenate(concatenate(rq, pq), yq); + } + inline quaternion make_quaternion_from_rotation_matrix(const float4x4 &mat) { + // https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion + float t = mat.m11 + mat.m22 + mat.m33; + if (t > 0) { + float r = ::std::sqrt(1.0f + t); + float s = 1.0f / (2.0f * r); + return { (mat.m23 - mat.m32) * s, (mat.m31 - mat.m13) * s, + (mat.m12 - mat.m21) * s, r * 0.5f }; + } else if (mat.m11 >= mat.m22 && mat.m11 >= mat.m33) { + float r = ::std::sqrt(1.0f + mat.m11 - mat.m22 - mat.m33); + float s = 1.0f / (2.0f * r); + return { r * 0.5f, (mat.m21 + mat.m12) * s, + (mat.m13 + mat.m31) * s, (mat.m23 - mat.m32) * s }; + } else if (mat.m22 >= mat.m11 && mat.m22 >= mat.m33) { + float r = ::std::sqrt(1.0f + mat.m22 - mat.m11 - mat.m33); + float s = 1.0f / (2.0f * r); + return { (mat.m21 + mat.m12) * s, r * 0.5f, + (mat.m32 + mat.m23) * s, (mat.m31 - mat.m13) * s }; + } else { + float r = ::std::sqrt(1.0f + mat.m33 - mat.m11 - mat.m22); + float s = 1.0f / (2.0f * r); + return { (mat.m13 + mat.m31) * s, (mat.m32 + mat.m23) * s, + r * 0.5f, (mat.m12 - mat.m21) * s }; + } + } inline bool is_identity(const quaternion &val) { return val == quaternion::identity(); } @@ -1002,9 +1118,11 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { return ::std::sqrt(length_squared(val)); } inline float length_squared(const quaternion &val) { - return val.x * val.x + val.y * val.y + val.z * val.z + val.w * val.w; + return dot(val, val); + } + inline float dot(const quaternion &val1, const quaternion &val2) { + return val1.x * val2.x + val1.y * val2.y + val1.z * val2.z + val1.w * val2.w; } - inline float dot(const quaternion &val1, const quaternion &val2); inline quaternion normalize(const quaternion &val) { return operator*(val, 1.0f / length(val)); } @@ -1014,9 +1132,40 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ { inline quaternion inverse(const quaternion &val) { return operator*(conjugate(val), 1.0f / length_squared(val)); } - inline quaternion slerp(const quaternion &val1, const quaternion &val2, float amount); - inline quaternion lerp(const quaternion &val1, const quaternion &val2, float amount); - inline quaternion concatenate(const quaternion &val1, const quaternion &val2); + inline quaternion slerp(const quaternion &val1, const quaternion &val2, float amount) { + // https://en.wikipedia.org/wiki/Slerp#Geometric_Slerp + float cosangle = dot(val1, val2); + quaternion end = val2; + if (cosangle < 0.0f) { + end = -val2; + cosangle = -cosangle; + } + float fact1, fact2; + const float epsilon = 1.0e-6f; + if (cosangle > 1.0f - epsilon) { + // Very small rotation range, or non-normalized input quaternions. + fact1 = (1.0f - amount); + fact2 = amount; + } else { + float angle = ::std::acos(cosangle); + float sinangle = ::std::sin(angle); + fact1 = ::std::sin((1.0f - amount) * angle) / sinangle; + fact2 = ::std::sin(amount * angle) / sinangle; + } + return val1 * fact1 + end * fact2; + } + inline quaternion lerp(const quaternion &val1, const quaternion &val2, float amount) { + quaternion end = val2; + if (dot(val1, val2) < 0.0f) + end = -val2; + return normalize(quaternion( + _impl::lerp(val1.x, end.x, amount), _impl::lerp(val1.y, end.y, amount), + _impl::lerp(val1.z, end.z, amount), _impl::lerp(val1.w, end.w, amount) + )); + } + inline quaternion concatenate(const quaternion &val1, const quaternion &val2) { + return val2 * val1; + } } _WINDOWS_NUMERICS_END_NAMESPACE_ diff --git a/mingw-w64-headers/testcases/test_windowsnumerics.cpp b/mingw-w64-headers/testcases/test_windowsnumerics.cpp index e4c9ed5b0..b33720953 100644 --- a/mingw-w64-headers/testcases/test_windowsnumerics.cpp +++ b/mingw-w64-headers/testcases/test_windowsnumerics.cpp @@ -25,6 +25,8 @@ 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; @@ -377,6 +379,8 @@ TEST_CASE("float2-functions") { 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 === @@ -474,6 +478,8 @@ TEST_CASE("float3-functions") { 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 === @@ -573,6 +579,12 @@ TEST_CASE("float4-functions") { 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 === @@ -872,6 +884,42 @@ TEST_CASE("float4x4-functions") { 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 + )); + const float3 scale = { 1.0f, 2.0f, 3.0f }; + const float3 translate = { 4.0f, 5.0f, 6.0f }; + const float4x4 v3 = make_float4x4_scale(scale) * make_float4x4_from_quaternion(q1) * make_float4x4_translation(translate); + float3 out_scale, out_translate; + quaternion out_rot; + CHECK(decompose(v3, &out_scale, &out_rot, &out_translate)); + CHECK_THAT(out_scale, eq3(scale)); + CHECK_THAT(out_rot, eqq(q1)); + CHECK_THAT(out_translate, eq3(translate)); + const float4x4 v4 = make_float4x4_from_quaternion(q1) * make_float4x4_scale(scale) * make_float4x4_translation(translate); + CHECK(!decompose(v4, &out_scale, &out_rot, &out_translate)); } // === plane === @@ -896,6 +944,8 @@ 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 === @@ -946,7 +996,70 @@ TEST_CASE("quaternion-functions") { 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)); - // TODO + 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)); } -- 2.25.1 _______________________________________________ Mingw-w64-public mailing list Mingw-w64-public@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mingw-w64-public