From 3c8fd70c125a10477d31c40536cc11777ad80d2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 1 Feb 2023 20:38:28 +0100 Subject: [PATCH] Animation: make Easing a template struct, add an Easingd typedef. Similarly to how Constants and Constantsd are made. --- doc/changelog.dox | 13 ++ doc/generated/easings.cpp | 44 +++-- src/Magnum/Animation/Easing.h | 231 ++++++++++++----------- src/Magnum/Animation/Test/EasingTest.cpp | 135 +++++++++---- 4 files changed, 252 insertions(+), 171 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 58fe63dc9..f0bdfcbe1 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -393,6 +393,13 @@ See also: @relativeref{Timeline,previousFrameDuration()} (see [mosra/magnum#604](https://github.com/mosra/magnum/pull/604)) +@subsubsection changelog-latest-changes-animation Animation library + +- Added a @ref Animation::Easingd typedef with + @ref Animation::BasicEasing "easing functions" implemented with + double precision, in addition to single-precision @ref Animation::Easing, + and similar in spirit to the @ref Constants and @ref Constantsd typedefs. + @subsubsection changelog-latest-changes-debugtools DebugTools library - @ref DebugTools::bufferData() and @ref DebugTools::bufferSubData() now @@ -1146,6 +1153,12 @@ See also: @ref Corrade::Containers::ArrayView are now removed. This should have a significant positive effect on compile times of code using the @ref GL, @ref Audio, @ref Trade and @ref Text libraries +- @ref Animation::Easing is now a typedef to a new + @ref Animation::BasicEasing struct instead of being a namespace in order to + expose the easing functions in double precision as @ref Animation::Easingd. + The change is API-compatible and shouldn't require any changes from user + side, however existing code that contained + @cpp using namespace Animation::Easing @ce will break. - As part of the ongoing STL header dependency cleanup, the following breaking changes related to @ref std::string, @ref std::vector and @ref std::pair are done: diff --git a/doc/generated/easings.cpp b/doc/generated/easings.cpp index 963de3c0c..292bc8e49 100644 --- a/doc/generated/easings.cpp +++ b/doc/generated/easings.cpp @@ -202,9 +202,7 @@ const Color3 success = 0x3bd267_srgbf; /** @todo better bezier approximations for more complex curves, easings.net has it awful */ int main() { - using namespace Animation::Easing; - - #define _c(name) Utility::String::lowercase(std::string{#name}), name + #define _c(name) Utility::String::lowercase(std::string{#name}), Animation::Easing::name generate(_c(linear), {}, /* [linear] */ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f}, @@ -212,127 +210,127 @@ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f}, /* [linear] */ , success, success); generate(_c(step), {}, {}, success, success); - generate(_c(smoothstep), {smootherstep}, + generate(_c(smoothstep), {Animation::Easing::smootherstep}, /* [smoothstep] */ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 0.0f}, Vector2{2.0f/3.0f, 1.0f}, Vector2{1.0f}} /* [smoothstep] */ , success, success); - generate(_c(smootherstep), {smoothstep}, {}, success, success); - generate(_c(quadraticIn), {cubicIn, quarticIn, quinticIn}, + generate(_c(smootherstep), {Animation::Easing::smoothstep}, {}, success, success); + generate(_c(quadraticIn), {Animation::Easing::cubicIn, Animation::Easing::quarticIn, Animation::Easing::quinticIn}, /* [quadraticIn] */ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 0.0f}, Vector2{2.0f/3.0f, 1.0f/3.0f}, Vector2{1.0f}} /* [quadraticIn] */ , danger, success); - generate(_c(quadraticOut), {cubicOut, quarticOut, quinticOut}, + generate(_c(quadraticOut), {Animation::Easing::cubicOut, Animation::Easing::quarticOut, Animation::Easing::quinticOut}, /* [quadraticOut] */ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 2.0f/3.0f}, Vector2{2.0f/3.0f, 1.0f}, Vector2{1.0f}} /* [quadraticOut] */ , success, danger); - generate(_c(quadraticInOut), {cubicInOut, quarticInOut, quinticInOut}, + generate(_c(quadraticInOut), {Animation::Easing::cubicInOut, Animation::Easing::quarticInOut, Animation::Easing::quinticInOut}, /* [quadraticInOut] */ CubicBezier2D{Vector2{0.0f}, Vector2{0.455f, 0.0f}, Vector2{0.545f, 1.0f}, Vector2{1.0f}} /* [quadraticInOut] */ ); - generate(_c(cubicIn), {quadraticIn, quarticIn, quinticIn}, + generate(_c(cubicIn), {Animation::Easing::quadraticIn, Animation::Easing::quarticIn, Animation::Easing::quinticIn}, /* [cubicIn] */ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 0.0f}, Vector2{2.0f/3.0f, 0.0f}, Vector2{1.0f}} /* [cubicIn] */ , danger, success); - generate(_c(cubicOut), {quadraticOut, quarticOut, quinticOut}, + generate(_c(cubicOut), {Animation::Easing::quadraticOut, Animation::Easing::quarticOut, Animation::Easing::quinticOut}, /* [cubicOut] */ CubicBezier2D{Vector2{0.0f}, Vector2{1.0f/3.0f, 1.0f}, Vector2{2.0f/3.0f, 1.0f}, Vector2{1.0f}} /* [cubicOut] */ , success, danger); - generate(_c(cubicInOut), {quadraticInOut, quarticInOut, quinticInOut}, + generate(_c(cubicInOut), {Animation::Easing::quadraticInOut, Animation::Easing::quarticInOut, Animation::Easing::quinticInOut}, /* [cubicInOut] */ CubicBezier2D{Vector2{0.0f}, Vector2{0.645f, 0.0f}, Vector2{0.355f, 1.0f}, Vector2{1.0f}} /* [cubicInOut] */ ); - generate(_c(quarticIn), {quadraticIn, cubicIn, quinticIn}, {} + generate(_c(quarticIn), {Animation::Easing::quadraticIn, Animation::Easing::cubicIn, Animation::Easing::quinticIn}, {} // , // /* [quarticIn] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.895f, 0.03f}, // Vector2{0.685f, 0.22f}, Vector2{1.0f}} // /* [quarticIn] */ , danger, success); - generate(_c(quarticOut), {quadraticOut, cubicOut, quinticOut}, {} + generate(_c(quarticOut), {Animation::Easing::quadraticOut, Animation::Easing::cubicOut, Animation::Easing::quinticOut}, {} // , // /* [quarticOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.165f, 0.84f}, // Vector2{0.44f, 1.0f}, Vector2{1.0f}} // /* [quarticOut] */ , success, danger); - generate(_c(quarticInOut), {quadraticInOut, cubicInOut, quinticInOut} + generate(_c(quarticInOut), {Animation::Easing::quadraticInOut, Animation::Easing::cubicInOut, Animation::Easing::quinticInOut} // , // /* [quarticInOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.77f, 0.0f}, // Vector2{0.175f, 1.0f}, Vector2{1.0f}} // /* [quarticInOut] */ ); - generate(_c(quinticIn), {quadraticIn, cubicIn, quarticIn}, {} + generate(_c(quinticIn), {Animation::Easing::quadraticIn, Animation::Easing::cubicIn, Animation::Easing::quarticIn}, {} // , // /* [quinticIn] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.755f, 0.05f}, // Vector2{0.855f, 0.06f}, Vector2{1.0f}} // /* [quinticIn] */ , danger, success); - generate(_c(quinticOut), {quadraticOut, cubicOut, quarticOut}, {} + generate(_c(quinticOut), {Animation::Easing::quadraticOut, Animation::Easing::cubicOut, Animation::Easing::quarticOut}, {} // , // /* [quinticOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.23f, 1.0f}, // Vector2{0.32f, 1.0f}, Vector2{1.0f}} // /* [quinticOut] */ , success, danger); - generate(_c(quinticInOut), {quadraticInOut, cubicInOut, quarticInOut} + generate(_c(quinticInOut), {Animation::Easing::quadraticInOut, Animation::Easing::cubicInOut, Animation::Easing::quarticInOut} // , // /* [quinticInOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.86f, 0.0f}, // Vector2{0.07f, 1.0f}, Vector2{1.0f}} // /* [quinticInOut] */ ); - generate(_c(sineIn), {circularIn} + generate(_c(sineIn), {Animation::Easing::circularIn} // , // /* [sineIn] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.47f, 0.0f}, // Vector2{0.745f, 0.715f}, Vector2{1.0f}} // /* [sineIn] */ ); - generate(_c(sineOut), {circularOut} + generate(_c(sineOut), {Animation::Easing::circularOut} // , // /* [sineOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.39f, 0.575f}, // Vector2{0.565f, 1.0f}, Vector2{1.0f}} // /* [sineOut] */ ); - generate(_c(sineInOut), {circularInOut} + generate(_c(sineInOut), {Animation::Easing::circularInOut} // , // /* [sineInOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.445f, 0.05f}, // Vector2{0.55f, 0.95f}, Vector2{1.0f}} // /* [sineInOut] */ ); - generate(_c(circularIn), {sineIn} + generate(_c(circularIn), {Animation::Easing::sineIn} // , // /* [circularIn] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.6f, 0.04f}, // Vector2{0.98f, 0.335f}, Vector2{1.0f}} // /* [circularIn] */ ); - generate(_c(circularOut), {sineOut} + generate(_c(circularOut), {Animation::Easing::sineOut} // , // /* [circularOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.075f, 0.085f}, // Vector2{0.165f, 1.0f}, Vector2{1.0f}} // /* [circularOut] */ ); - generate(_c(circularInOut), {sineInOut} + generate(_c(circularInOut), {Animation::Easing::sineInOut} // , // /* [circularInOut] */ // CubicBezier2D{Vector2{0.0f}, Vector2{0.785f, 0.135f}, diff --git a/src/Magnum/Animation/Easing.h b/src/Magnum/Animation/Easing.h index e5b670c63..c79ee18ed 100644 --- a/src/Magnum/Animation/Easing.h +++ b/src/Magnum/Animation/Easing.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Namespace @ref Magnum::Animation::Easing + * @brief Struct @ref Magnum::Animation::BasicEasing, typedef @ref Magnum::Animation::Easing, @ref Magnum::Animation::Easingd */ #include "Magnum/Magnum.h" @@ -37,11 +37,13 @@ namespace Magnum { namespace Animation { /** @brief Easing functions +@m_since_latest @m_keywords{Tweening Inbetweening} A collection of predefined [easing / tweening](https://en.wikipedia.org/wiki/Inbetweening) -functions for adding life to animation interpolation. +functions for adding life to animation interpolation. Meant to be used through +the @ref Easing and @ref Easingd typedefs. This library is built as part of Magnum by default. To use this library with CMake, find the `Magnum` package and link to the `Magnum::Magnum` target: @@ -298,7 +300,7 @@ https://easings.net/, [https://github.com/bkaradzic/bx](https://github.com/bkaradzic/bx/blob/master/include/bx/inline/easing.inl), https://blog.demofox.org/2014/08/28/one-dimensional-bezier-curves/. */ -namespace Easing { +template struct BasicEasing { /** * @brief Linear * @@ -312,14 +314,14 @@ namespace Easing { * * @snippet easings.cpp linear */ - inline Float linear(Float t) { return t; } + static T linear(T t) { return t; } /** * @brief Step * * Similar to @ref Math::select(), but does the step in the middle of the * range instead of at the end. Implementation matching the GLSL - * @glsl step() @ce function with @cpp edge = 0.5f @ce. + * @glsl step() @ce function with @cpp edge = T(0.5) @ce. * * @htmlinclude easings-step.svg * @@ -333,7 +335,7 @@ namespace Easing { * @m_keyword{step(),GLSL step(),} * @see @ref smoothstep(), @ref smootherstep() */ - inline Float step(Float t) { return t < 0.5f ? 0.0f : 1.0f; } + static T step(T t) { return t < T(0.5) ? T(0.0) : T(1.0); } /** * @brief [Smoothstep](https://en.wikipedia.org/wiki/Smoothstep). @@ -360,12 +362,12 @@ namespace Easing { * @m_keyword{smoothstep(),GLSL smoothstep(),} * @see @ref smootherstep(), @ref Math::clamp() */ - inline Float smoothstep(Float t) { + static T smoothstep(T t) { /* Deliberately *not* using Math::clamp() because that would drag in unneeded vector headers */ - if(t <= 0.0f) return 0.0f; - if(t >= 1.0f) return 1.0f; - return (3.0f - 2.0f*t)*t*t; + if(t <= T(0.0)) return T(0.0); + if(t >= T(1.0)) return T(1.0); + return (T(3.0) - T(2.0)*t)*t*t; } /** @@ -385,12 +387,12 @@ namespace Easing { * * @see @ref Math::clamp() */ - inline Float smootherstep(Float t) { + static T smootherstep(T t) { /* Deliberately *not* using Math::clamp() because that would drag in unneeded vector headers */ - if(t <= 0.0f) return 0.0f; - if(t >= 1.0f) return 1.0f; - return t*t*t*(t*(t* 6.0f - 15.0f) + 10.0f); + if(t <= T(0.0)) return T(0.0); + if(t >= T(1.0)) return T(1.0); + return t*t*t*(t*(t*T(6.0) - T(15.0)) + T(10.0)); } /** @@ -408,7 +410,7 @@ namespace Easing { * * @see @ref cubicIn(), @ref quarticIn(), @ref quinticIn() */ - inline Float quadraticIn(Float t) { return t*t; } + static T quadraticIn(T t) { return t*t; } /** * @brief Quadratic out @@ -425,7 +427,7 @@ namespace Easing { * * @see @ref cubicOut(), @ref quarticOut(), @ref quinticOut() */ - inline Float quadraticOut(Float t) { return -t*(t - 2.0f); } + static T quadraticOut(T t) { return -t*(t - T(2.0)); } /** * @brief Quadratic in and out @@ -447,11 +449,11 @@ namespace Easing { * * @see @ref cubicInOut(), @ref quarticInOut(), @ref quinticInOut() */ - inline Float quadraticInOut(Float t) { - if(t < 0.5f) return 2.0f*t*t; + static T quadraticInOut(T t) { + if(t < T(0.5)) return T(2.0)*t*t; - const Float inv = 1.0f - t; - return 1.0f - 2.0f*inv*inv; + const T inv = T(1.0) - t; + return T(1.0) - T(2.0)*inv*inv; } /** @@ -469,7 +471,7 @@ namespace Easing { * * @see @ref quadraticIn(), @ref quarticIn(), @ref quinticIn() */ - inline Float cubicIn(Float t) { return t*t*t; } + static T cubicIn(T t) { return t*t*t; } /** * @brief Cubic out @@ -486,9 +488,9 @@ namespace Easing { * * @see @ref quadraticOut(), @ref quarticOut(), @ref quinticOut() */ - inline Float cubicOut(Float t) { - const Float inv = t - 1.0f; - return inv*inv*inv + 1.0f; + static T cubicOut(T t) { + const T inv = t - T(1.0); + return inv*inv*inv + T(1.0); } /** @@ -511,11 +513,11 @@ namespace Easing { * * @see @ref quadraticInOut(), @ref quarticInOut(), @ref quinticInOut() */ - inline Float cubicInOut(Float t) { - if(t < 0.5f) return 4.0f*t*t*t; + static T cubicInOut(T t) { + if(t < T(0.5)) return T(4.0)*t*t*t; - const Float inv = 1.0f - t; - return 1.0f - 4.0f*inv*inv*inv; + const T inv = T(1.0) - t; + return T(1.0) - T(4.0)*inv*inv*inv; } /** @@ -529,11 +531,11 @@ namespace Easing { * * @see @ref quadraticIn(), @ref cubicIn(), @ref quinticIn() */ - inline Float quarticIn(Float t) { + static T quarticIn(T t) { /* Not just t*t*t*t, since compile can't optimize it on its own to just two multiplication without breaking precision. So doing that explicitly. */ - const Float tt = t*t; + const T tt = t*t; return tt*tt; } @@ -548,11 +550,11 @@ namespace Easing { * * @see @ref quadraticOut(), @ref cubicOut(), @ref quinticOut() */ - inline Float quarticOut(Float t) { + static T quarticOut(T t) { /* Instead of t*t*t*t suggesting the optimization as described above */ - const Float inv = 1.0f - t; - const Float quad = inv*inv; - return 1.0f - quad*quad; + const T inv = T(1.0) - t; + const T quad = inv*inv; + return T(1.0) - quad*quad; } /** @@ -571,17 +573,17 @@ namespace Easing { * * @see @ref quadraticInOut(), @ref cubicInOut(), @ref quinticInOut() */ - inline Float quarticInOut(Float t) { + static T quarticInOut(T t) { /* Instead of t*t*t*t suggesting the optimization as described above */ - if(t < 0.5f) { - const Float tt = t*t; + if(t < T(0.5)) { + const T tt = t*t; return 8*tt*tt; } - const Float inv = 1.0f - t; - const Float quad = inv*inv; - return 1.0f - 8.0f*quad*quad; + const T inv = T(1.0) - t; + const T quad = inv*inv; + return T(1.0) - T(8.0)*quad*quad; } /** @@ -595,10 +597,10 @@ namespace Easing { * * @see @ref quadraticIn(), @ref cubicIn(), @ref quarticIn() */ - inline Float quinticIn(Float t) { + static T quinticIn(T t) { /* Instead of t*t*t*t*t suggesting the optimization as described above */ - const Float tt = t*t; + const T tt = t*t; return tt*t*tt; } @@ -613,12 +615,12 @@ namespace Easing { * * @see @ref quadraticOut(), @ref cubicOut(), @ref quarticOut() */ - inline Float quinticOut(Float t) { + static T quinticOut(T t) { /* Instead of t*t*t*t*t suggesting the optimization as described above */ - const Float inv = t - 1.0f; - const Float quad = inv*inv; - return 1.0f + quad*inv*quad; + const T inv = t - T(1.0); + const T quad = inv*inv; + return T(1.0) + quad*inv*quad; } /** @@ -637,18 +639,18 @@ namespace Easing { * * @see @ref quadraticInOut(), @ref cubicInOut(), @ref quarticInOut() */ - inline Float quinticInOut(Float t) { + static T quinticInOut(T t) { /* Instead of t*t*t*t*t suggesting the optimization as described above */ - if(t < 0.5f) { - const Float tt = t*t; - return 16.0f*tt*t*tt; + if(t < T(0.5)) { + const T tt = t*t; + return T(16.0)*tt*t*tt; } - const Float inv = 1.0f - t; - const Float quad = inv*inv; - return 1.0f - 16.0f*quad*inv*quad; + const T inv = T(1.0) - t; + const T quad = inv*inv; + return T(1.0) - T(16.0)*quad*inv*quad; } /** @@ -662,8 +664,8 @@ namespace Easing { * * @see @ref circularIn() */ - inline Float sineIn(Float t) { - return 1.0f + std::sin(Constants::piHalf()*(t - 1.0f)); + static T sineIn(T t) { + return T(1.0) + std::sin(Math::Constants::piHalf()*(t - T(1.0))); } /** @@ -677,8 +679,8 @@ namespace Easing { * * @see @ref circularOut() */ - inline Float sineOut(Float t) { - return std::sin(Constants::piHalf()*t); + static T sineOut(T t) { + return std::sin(Math::Constants::piHalf()*t); } /** @@ -694,8 +696,8 @@ namespace Easing { * * @see @ref circularInOut() */ - inline Float sineInOut(Float t) { - return 0.5f*(1.0f - std::cos(t*Constants::pi())); + static T sineInOut(T t) { + return T(0.5)*(T(1.0) - std::cos(t*Math::Constants::pi())); } /** @@ -709,8 +711,8 @@ namespace Easing { * * @see @ref sineIn() */ - inline Float circularIn(Float t) { - return 1.0f - std::sqrt(1.0f - t*t); + static T circularIn(T t) { + return T(1.0) - std::sqrt(T(1.0) - t*t); } /** @@ -724,8 +726,8 @@ namespace Easing { * * @see @ref sineOut() */ - inline Float circularOut(Float t) { - return std::sqrt((2.0f - t)*t); + static T circularOut(T t) { + return std::sqrt((T(2.0) - t)*t); } /** @@ -744,9 +746,9 @@ namespace Easing { * * @see @ref sineInOut() */ - inline Float circularInOut(Float t) { - if(t < 0.5f) return 0.5f*(1.0f - std::sqrt(1.0f - 4.0f*t*t)); - return 0.5f*(1.0f + std::sqrt(-4.0f*t*t + 8.0f*t - 3.0f)); + static T circularInOut(T t) { + if(t < T(0.5)) return T(0.5)*(T(1.0) - std::sqrt(T(1.0) - T(4.0)*t*t)); + return T(0.5)*(T(1.0) + std::sqrt(T(-4.0)*t*t + T(8.0)*t - T(3.0))); } /** @@ -768,8 +770,8 @@ namespace Easing { * @todo could be done better like with the sRGB curve, but should I * bother? */ - inline Float exponentialIn(Float t) { - return t <= 0.0f ? 0.0f : std::pow(2.0f, 10.0f * (t - 1.0f)); + static T exponentialIn(T t) { + return t <= T(0.0) ? T(0.0) : std::pow(T(2.0), T(10.0)*(t - T(1.0))); } /** @@ -791,8 +793,8 @@ namespace Easing { * @todo could be done better like with the sRGB curve, but should I * bother? */ - inline Float exponentialOut(Float t) { - return t >= 1.0f ? 1.0f : 1.0f - std::pow(2.0f, -10.0f*t); + static T exponentialOut(T t) { + return t >= T(1.0) ? T(1.0) : T(1.0) - std::pow(T(2.0), -T(10.0)*t); } /** @@ -814,11 +816,11 @@ namespace Easing { * \end{cases} * @f] */ - inline Float exponentialInOut(Float t) { - if(t <= 0.0f) return 0.0f; - if(t < 0.5f) return 0.5f*std::pow(2.0f, 20.0f*t - 10.0f); - if(t < 1.0f) return 1.0f - 0.5f*std::pow(2.0f, 10.0f - 20.0f*t); - return 1.0f; + static T exponentialInOut(T t) { + if(t <= T(0.0)) return T(0.0); + if(t < T(0.5)) return T(0.5)*std::pow(T(2.0), T(20.0)*t - T(10.0)); + if(t < T(1.0)) return T(1.0) - T(0.5)*std::pow(T(2.0), T(10.0) -T(20.0)*t); + return T(1.0); } /** @@ -832,8 +834,8 @@ namespace Easing { * y = 2^{10 (p - 1)} \sin(13 \frac{\pi}{2} x) * @f] */ - inline Float elasticIn(Float t) { - return std::pow(2.0f, 10.0f*(t - 1.0f))*std::sin(13.0f*Constants::piHalf()*t); + static T elasticIn(T t) { + return std::pow(T(2.0), T(10.0)*(t - T(1.0)))*std::sin(T(13.0)*Math::Constants::piHalf()*t); } /** @@ -847,8 +849,8 @@ namespace Easing { * y = 1 - 2^{-10 x} \sin(13 \frac{\pi}{2} (x + 1)) * @f] */ - inline Float elasticOut(Float t) { - return 1.0f - std::pow(2.0f, -10.0f*t)*std::sin(13.0f*Constants::piHalf()*(t + 1.0f)); + static T elasticOut(T t) { + return T(1.0) - std::pow(T(2.0), T(-10.0)*t)*std::sin(T(13.0)*Math::Constants::piHalf()*(t + T(1.0))); } /** @@ -866,10 +868,10 @@ namespace Easing { * \end{cases} * @f] */ - inline Float elasticInOut(Float t) { - if(t < 0.5f) - return 0.5f*std::pow(2.0f, 10.0f*(2.0f*t - 1.0f))*std::sin(13.0f*Constants::pi()*t); - return 1.0f - 0.5f*std::pow(2.0f, 10.0f*(1.0f - 2.0f*t))*std::sin(13.0f*Constants::pi()*t); + static T elasticInOut(T t) { + if(t < T(0.5)) + return T(0.5)*std::pow(T(2.0), T(10.0)*(T(2.0)*t - T(1.0)))*std::sin(T(13.0)*Math::Constants::pi()*t); + return T(1.0) - T(0.5)*std::pow(T(2.0), T(10.0)*(T(1.0) - T(2.0)*t))*std::sin(T(13.0)*Math::Constants::pi()*t); } /** @@ -881,8 +883,8 @@ namespace Easing { * y = x^3 - x \sin(\pi x) * @f] */ - inline Float backIn(Float t) { - return t*(t*t - std::sin(Constants::pi()*t)); + static T backIn(T t) { + return t*(t*t - std::sin(Math::Constants::pi()*t)); } /** @@ -894,9 +896,9 @@ namespace Easing { * y = 1 - ((1 - x)^3 - (1 - x) \sin(\pi (1 - x))) * @f] */ - inline Float backOut(Float t) { - const Float inv = 1.0f - t; - return 1 - inv*(inv*inv - std::sin(Constants::pi()*inv)); + static T backOut(T t) { + const T inv = T(1.0) - t; + return 1 - inv*(inv*inv - std::sin(Math::Constants::pi()*inv)); } /** @@ -915,27 +917,23 @@ namespace Easing { * \end{cases} * @f] */ - inline Float backInOut(Float t) { - if(t < 0.5f) { - const Float t2 = 2.0f*t; - return 0.5f*t2*(t2*t2 - std::sin(Constants::pi()*t2)); + static T backInOut(T t) { + if(t < T(0.5)) { + const T t2 = T(2.0)*t; + return T(0.5)*t2*(t2*t2 - std::sin(Math::Constants::pi()*t2)); } - const Float inv = 2.0f - 2.0f*t; - return 1.0f - 0.5f*inv*(inv*inv - std::sin(Constants::pi()*inv)); + const T inv = T(2.0) - T(2.0)*t; + return T(1.0) - T(0.5)*inv*(inv*inv - std::sin(Math::Constants::pi()*inv)); } - #ifndef DOXYGEN_GENERATING_OUTPUT - Float bounceOut(Float); - #endif - /** * @brief Bounce in * * @htmlinclude easings-bouncein.svg */ - inline Float bounceIn(Float t) { - return 1.0f - bounceOut(1.0f - t); + static T bounceIn(T t) { + return T(1.0) - bounceOut(T(1.0) - t); } /** @@ -943,11 +941,11 @@ namespace Easing { * * @htmlinclude easings-bounceout.svg */ - inline Float bounceOut(Float t) { - if(t < 4.0f/11.0f) return (121.0f*t*t)/16.0f; - if(t < 8.0f/11.0f) return 363.0f/40.0f*t*t - 99.0f/10.0f*t + 17.0f/5.0f; - if(t < 9.0f/10.0f) return 4356.0f/361.0f*t*t - 35442.0f/1805.0f*t + 16061.0f/1805.0f; - return 54.0f/5.0f*t*t - 513.0f/25.0f*t + 268.0f/25.0f; + static T bounceOut(T t) { + if(t < T(4.0)/T(11.0)) return (T(121.0)*t*t)/T(16.0); + if(t < T(8.0)/T(11.0)) return T(363.0)/T(40.0)*t*t - T(99.0)/T(10.0)*t + T(17.0)/T(5.0); + if(t < T(9.0)/T(10.0)) return T(4356.0)/T(361.0)*t*t - T(35442.0)/T(1805.0)*t + T(16061.0)/T(1805.0); + return T(54.0)/T(5.0)*t*t - T(513.0)/T(25.0)*t + T(268.0)/T(25.0); } /** @@ -957,11 +955,26 @@ namespace Easing { * * @htmlinclude easings-bounceinout.svg */ - inline Float bounceInOut(Float t) { - if(t < 0.5f) return 0.5f*bounceIn(2.0f*t); - return 0.5f*bounceOut(2.0f*t - 1.0f) + 0.5f; + static T bounceInOut(T t) { + if(t < T(0.5)) return T(0.5)*bounceIn(T(2.0)*t); + return T(0.5)*bounceOut(T(2.0)*t - T(1.0)) + T(0.5); } -} +}; + +/** +@brief Float easing functions + +@see @ref Easingd +*/ +typedef BasicEasing Easing; + +/** +@brief Double easing functions +@m_since_latest + +@see @ref Easing +*/ +typedef BasicEasing Easingd; }} diff --git a/src/Magnum/Animation/Test/EasingTest.cpp b/src/Magnum/Animation/Test/EasingTest.cpp index 223f185b5..9679c42d8 100644 --- a/src/Magnum/Animation/Test/EasingTest.cpp +++ b/src/Magnum/Animation/Test/EasingTest.cpp @@ -30,24 +30,26 @@ #include #include "Magnum/Animation/Easing.h" +#include "Magnum/Math/TypeTraits.h" namespace Magnum { namespace Animation { namespace Test { namespace { struct EasingTest: TestSuite::Tester { explicit EasingTest(); - void bounds(); - void monotonicity(); - void symmetry(); - void values(); + template void bounds(); + template void monotonicity(); + template void symmetry(); + template void values(); - void benchmark(); + template void benchmark(); }; -#define _c(name) #name, Easing::name -constexpr struct { +#define _c(name) #name, Easing::name, Easingd::name +constexpr struct Bounds { const char* name; Float(*function)(Float); + Double(*functiond)(Double); } BoundsData[] { {_c(linear)}, {_c(step)}, @@ -80,9 +82,10 @@ constexpr struct { {_c(bounceInOut)} }; -constexpr struct { +constexpr struct Monotonicity { const char* name; Float(*function)(Float); + Double(*functiond)(Double); } MonotonicityData[] { {_c(linear)}, {_c(step)}, @@ -112,11 +115,13 @@ constexpr struct { /* elastic, back and bounce are not monotonic */ }; -constexpr struct { +constexpr struct Symmetry { const char* name; Float(*function)(Float); + Double(*functiond)(Double); const char* symmetricName; Float(*symmetric)(Float); + Double(*symmetricd)(Double); } SymmetryData[] { {_c(linear), _c(linear)}, {_c(step), _c(step)}, @@ -144,9 +149,10 @@ constexpr struct { {_c(bounceInOut), _c(bounceInOut)} }; -constexpr struct { +constexpr struct Value { const char* name; Float(*function)(Float); + Double(*functiond)(Double); Float values[3]; } ValueData[] { {_c(linear), {0.25f, 0.5f, 0.75f}}, @@ -186,91 +192,142 @@ constexpr struct { }; #undef _c +template struct FunctionFor; +template<> struct FunctionFor { + static auto function(const Bounds& s) -> Float(*)(Float) { + return s.function; + } + static auto function(const Monotonicity& s) -> Float(*)(Float) { + return s.function; + } + static auto function(const Symmetry& s) -> Float(*)(Float) { + return s.function; + } + static auto symmetric(const Symmetry& s) -> Float(*)(Float) { + return s.symmetric; + } + static auto function(const Value& s) -> Float(*)(Float) { + return s.function; + } +}; +template<> struct FunctionFor { + static auto function(const Bounds& s) -> Double(*)(Double) { + return s.functiond; + } + static auto function(const Monotonicity& s) -> Double(*)(Double) { + return s.functiond; + } + static auto function(const Symmetry& s) -> Double(*)(Double) { + return s.functiond; + } + static auto symmetric(const Symmetry& s) -> Double(*)(Double) { + return s.symmetricd; + } + static auto function(const Value& s) -> Double(*)(Double) { + return s.functiond; + } +}; + EasingTest::EasingTest() { - addInstancedTests({&EasingTest::bounds}, + addInstancedTests({ + &EasingTest::bounds, + &EasingTest::bounds}, Containers::arraySize(BoundsData)); - addInstancedTests({&EasingTest::monotonicity}, + addInstancedTests({ + &EasingTest::monotonicity, + &EasingTest::monotonicity}, Containers::arraySize(MonotonicityData)); - addInstancedTests({&EasingTest::symmetry}, + addInstancedTests({ + &EasingTest::symmetry, + &EasingTest::symmetry}, Containers::arraySize(SymmetryData)); - addInstancedTests({&EasingTest::values}, + addInstancedTests({ + &EasingTest::values, + &EasingTest::values}, Containers::arraySize(ValueData)); - addInstancedBenchmarks({&EasingTest::benchmark}, 100, + addInstancedBenchmarks({ + &EasingTest::benchmark, + &EasingTest::benchmark}, 100, Containers::arraySize(ValueData)); } enum: std::size_t { PropertyVerificationStepCount = 50 }; -void EasingTest::bounds() { +template void EasingTest::bounds() { auto&& data = BoundsData[testCaseInstanceId()]; setTestCaseDescription(data.name); + setTestCaseTemplateName(Math::TypeTraits::name()); - Float scale = 1.0f/Float(PropertyVerificationStepCount - 1); + T scale = T(1.0)/T(PropertyVerificationStepCount - 1); for(std::size_t i = 0; i != PropertyVerificationStepCount; ++i) { - Float t = i*scale; - CORRADE_COMPARE_AS(data.function(t), 0.0f, TestSuite::Compare::GreaterOrEqual); - CORRADE_COMPARE_AS(data.function(t), 1.0f, TestSuite::Compare::LessOrEqual); + T t = i*scale; + CORRADE_COMPARE_AS(FunctionFor::function(data)(t), T(0.0), TestSuite::Compare::GreaterOrEqual); + CORRADE_COMPARE_AS(FunctionFor::function(data)(t), T(1.0), TestSuite::Compare::LessOrEqual); } } -void EasingTest::monotonicity() { +template void EasingTest::monotonicity() { auto&& data = MonotonicityData[testCaseInstanceId()]; setTestCaseDescription(data.name); + setTestCaseTemplateName(Math::TypeTraits::name()); - Float scale = 1.0f/Float(PropertyVerificationStepCount - 1); - Float prev = data.function(0); + T scale = T(1.0)/T(PropertyVerificationStepCount - 1); + T prev = FunctionFor::function(data)(0); for(std::size_t i = 1; i != PropertyVerificationStepCount; ++i) { - Float cur = data.function(i*scale); + T cur = FunctionFor::function(data)(i*scale); CORRADE_COMPARE_AS(cur, prev, TestSuite::Compare::GreaterOrEqual); prev = cur; } } -void EasingTest::symmetry() { +template void EasingTest::symmetry() { auto&& data = SymmetryData[testCaseInstanceId()]; if(data.name == Containers::StringView{data.symmetricName}) setTestCaseDescription(data.name); else setTestCaseDescription(Utility::format("{} : {}", data.name, data.symmetricName)); + setTestCaseTemplateName(Math::TypeTraits::name()); /* Not testing the edges, as these are tested in values() anyway (and are problematic in functions that have explicit handling for them) */ - Float scale = 1.0f/Float(PropertyVerificationStepCount + 1); - std::size_t max = PropertyVerificationStepCount/(data.function == data.symmetric ? 2 : 1); + T scale = T(1.0)/T(PropertyVerificationStepCount + 1); + std::size_t max = PropertyVerificationStepCount/(FunctionFor::function(data) == FunctionFor::symmetric(data) ? 2 : 1); for(std::size_t i = 1; i != max; ++i) { - Float t = i*scale; - CORRADE_COMPARE(data.function(t), 1.0f - data.symmetric(1.0f - t)); + T t = i*scale; + CORRADE_COMPARE(FunctionFor::function(data)(t), T(1.0) - FunctionFor::symmetric(data)(T(1.0) - t)); } } -void EasingTest::values() { +template void EasingTest::values() { auto&& data = ValueData[testCaseInstanceId()]; setTestCaseDescription(data.name); + setTestCaseTemplateName(Math::TypeTraits::name()); - CORRADE_COMPARE(data.function(0.0f), 0.0f); - CORRADE_COMPARE(data.function(1.0f), 1.0f); - CORRADE_COMPARE(data.function(0.25f), data.values[0]); - CORRADE_COMPARE(data.function(0.50f), data.values[1]); - CORRADE_COMPARE(data.function(0.75f), data.values[2]); + CORRADE_COMPARE(FunctionFor::function(data)(T(0.0)), T(0.0)); + CORRADE_COMPARE(FunctionFor::function(data)(T(1.0)), T(1.0)); + CORRADE_COMPARE(FunctionFor::function(data)(T(0.25)), data.values[0]); + CORRADE_COMPARE(FunctionFor::function(data)(T(0.50)), data.values[1]); + CORRADE_COMPARE(FunctionFor::function(data)(T(0.75)), data.values[2]); } enum: Int { BenchmarkStepCount = 5000 }; -void EasingTest::benchmark() { +template void EasingTest::benchmark() { auto&& data = ValueData[testCaseInstanceId()]; setTestCaseDescription(data.name); + setTestCaseTemplateName(Math::TypeTraits::name()); /* Skip edges because the cumulated number may exceed 1 (on asm.js Emscripten), producing NaN on some functions and failing the test */ - Float scale = 1.0f/Float(BenchmarkStepCount + 1); - Float result = 0.0f; + T scale = T(1.0)/T(BenchmarkStepCount + 1); + T result = T(0.0); std::size_t i = 0; CORRADE_BENCHMARK(BenchmarkStepCount) - result += data.function(++i*scale); + result += FunctionFor::function(data)(++i*scale); /* backIn() has -340 */ CORRADE_COMPARE_AS(result, -350.0f, TestSuite::Compare::Greater);