diff --git a/src/Magnum/Animation/Easing.h b/src/Magnum/Animation/Easing.h index 12fc2081e..e5b670c63 100644 --- a/src/Magnum/Animation/Easing.h +++ b/src/Magnum/Animation/Easing.h @@ -299,663 +299,670 @@ https://easings.net/, https://blog.demofox.org/2014/08/28/one-dimensional-bezier-curves/. */ namespace Easing { + /** + * @brief Linear + * + * @htmlinclude easings-linear.svg + * + * @f[ + * y = x + * @f] + * + * One possible *exact* Bézier representation: + * + * @snippet easings.cpp linear + */ + inline Float linear(Float 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. + * + * @htmlinclude easings-step.svg + * + * @f[ + * y = \begin{cases} + * 0, & x < 0.5 \\ + * 1, & x \ge 0.5 + * \end{cases} + * @f] + * + * @m_keyword{step(),GLSL step(),} + * @see @ref smoothstep(), @ref smootherstep() + */ + inline Float step(Float t) { return t < 0.5f ? 0.0f : 1.0f; } + + /** + * @brief [Smoothstep](https://en.wikipedia.org/wiki/Smoothstep). + * + * Implementation matching the GLSL @glsl smoothstep() @ce function. + * Combine with @ref Math::lerp() to get the equivalent result: + * + * @snippet MagnumAnimation.cpp Easing-smoothstep + * + * @htmlinclude easings-smoothstep.svg + * + * @f[ + * y = \begin{cases} + * 0, & x < 0 \\ + * 3x^2 - 2x^3, & x \in [0, 1] \\ + * 1, & x > 1 + * \end{cases} + * @f] + * + * *Exact* Bézier representation: + * + * @snippet easings.cpp smoothstep + * + * @m_keyword{smoothstep(),GLSL smoothstep(),} + * @see @ref smootherstep(), @ref Math::clamp() + */ + inline Float smoothstep(Float 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; + } -/** -@brief Linear - -@htmlinclude easings-linear.svg - -@f[ - y = x -@f] - -One possible *exact* Bézier representation: - -@snippet easings.cpp linear - -*/ -inline Float linear(Float 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. - -@htmlinclude easings-step.svg - -@f[ - y = \begin{cases} - 0, & x < 0.5 \\ - 1, & x \ge 0.5 - \end{cases} -@f] - -@m_keyword{step(),GLSL step(),} -@see @ref smoothstep(), @ref smootherstep() -*/ -inline Float step(Float t) { return t < 0.5f ? 0.0f : 1.0f; } - -/** -@brief [Smoothstep](https://en.wikipedia.org/wiki/Smoothstep). - -Implementation matching the GLSL @glsl smoothstep() @ce function. Combine with -@ref Math::lerp() to get the equivalent result: - -@snippet MagnumAnimation.cpp Easing-smoothstep - -@htmlinclude easings-smoothstep.svg - -@f[ - y = \begin{cases} - 0, & x < 0 \\ - 3x^2 - 2x^3, & x \in [0, 1] \\ - 1, & x > 1 - \end{cases} -@f] - -* *Exact* Bézier representation: - -@snippet easings.cpp smoothstep - -@m_keyword{smoothstep(),GLSL smoothstep(),} -@see @ref smootherstep(), @ref Math::clamp() -*/ -inline Float smoothstep(Float 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; -} - -/** -@brief [Smootherstep](https://en.wikipedia.org/wiki/Smoothstep#Variations). - -Improved version of @ref smoothstep() by [Ken Perlin](https://en.wikipedia.org/wiki/Ken_Perlin). - -@htmlinclude easings-smootherstep.svg - -@f[ - y = \begin{cases} - 0, & x < 0 \\ - 6x^5 - 15x^4 + 10x^3, & x \in [0, 1] \\ - 1, & x > 1 - \end{cases} -@f] - -@see @ref Math::clamp() -*/ -inline Float smootherstep(Float 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); -} - -/** -@brief Quadratic in - -@htmlinclude easings-quadraticin.svg - -@f[ - y = x^2 -@f] - -* *Exact* Bézier representation: - -@snippet easings.cpp quadraticIn - -@see @ref cubicIn(), @ref quarticIn(), @ref quinticIn() -*/ -inline Float quadraticIn(Float t) { return t*t; } - -/** -@brief Quadratic out - -@htmlinclude easings-quadraticout.svg - -@f[ - y = 1 - (1 - x)^2 = (2 - x) x -@f] - -* *Exact* Bézier representation: - -@snippet easings.cpp quadraticOut - -@see @ref cubicOut(), @ref quarticOut(), @ref quinticOut() -*/ -inline Float quadraticOut(Float t) { return -t*(t - 2.0f); } - -/** -@brief Quadratic in and out - -Combination of @ref quadraticIn() and @ref quadraticOut(). - -@htmlinclude easings-quadraticinout.svg - -@f[ - y = \begin{cases} - 2 x^2, & x < 0.5 \\ - 1 - 2 (1 - x)^2, & x \ge 0.5 - \end{cases} -@f] - -Approximate Bézier representation: - -@snippet easings.cpp quadraticInOut - -@see @ref cubicInOut(), @ref quarticInOut(), @ref quinticInOut() -*/ -inline Float quadraticInOut(Float t) { - if(t < 0.5f) return 2.0f*t*t; - - const Float inv = 1.0f - t; - return 1.0f - 2.0f*inv*inv; -} - -/** -@brief Cubic in - -@htmlinclude easings-cubicin.svg - -@f[ - y = x^3 -@f] - -* *Exact* Bézier representation: - -@snippet easings.cpp cubicIn - -@see @ref quadraticIn(), @ref quarticIn(), @ref quinticIn() -*/ -inline Float cubicIn(Float t) { return t*t*t; } - -/** -@brief Cubic out - -@htmlinclude easings-cubicout.svg - -@f[ - y = 1 - (1 - x)^3 -@f] - -* *Exact* Bézier representation: - -@snippet easings.cpp cubicOut - -@see @ref quadraticOut(), @ref quarticOut(), @ref quinticOut() -*/ -inline Float cubicOut(Float t) { - const Float inv = t - 1.0f; - return inv*inv*inv + 1.0f; -} - -/** -@brief Cubic in and out - -Combination of @ref cubicIn() and @ref cubicOut(). - -@htmlinclude easings-cubicinout.svg - -@f[ - y = \begin{cases} - 4 x^3, & x < 0.5 \\ - 1 - 4 (1 - x)^3, & x \ge 0.5 - \end{cases} -@f] - -Approximate Bézier representation: - -@snippet easings.cpp cubicInOut - -@see @ref quadraticInOut(), @ref quarticInOut(), @ref quinticInOut() -*/ -inline Float cubicInOut(Float t) { - if(t < 0.5f) return 4.0f*t*t*t; - - const Float inv = 1.0f - t; - return 1.0f - 4.0f*inv*inv*inv; -} - -/** -@brief Quartic in - -@htmlinclude easings-quarticin.svg - -@f[ - y = x^4 -@f] - -@see @ref quadraticIn(), @ref cubicIn(), @ref quinticIn() -*/ -inline Float quarticIn(Float 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; - return tt*tt; -} - -/** -@brief Quartic out - -@htmlinclude easings-quarticout.svg - -@f[ - y = 1 - (1 - x)^4 -@f] - -@see @ref quadraticOut(), @ref cubicOut(), @ref quinticOut() -*/ -inline Float quarticOut(Float 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; -} - -/** -@brief Quartic in and out - -Combination of @ref quarticIn() and @ref quarticOut(). + /** + * @brief [Smootherstep](https://en.wikipedia.org/wiki/Smoothstep#Variations). + * + * Improved version of @ref smoothstep() by [Ken Perlin](https://en.wikipedia.org/wiki/Ken_Perlin). + * + * @htmlinclude easings-smootherstep.svg + * + * @f[ + * y = \begin{cases} + * 0, & x < 0 \\ + * 6x^5 - 15x^4 + 10x^3, & x \in [0, 1] \\ + * 1, & x > 1 + * \end{cases} + * @f] + * + * @see @ref Math::clamp() + */ + inline Float smootherstep(Float 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); + } -@htmlinclude easings-quarticinout.svg + /** + * @brief Quadratic in + * + * @htmlinclude easings-quadraticin.svg + * + * @f[ + * y = x^2 + * @f] + * + * *Exact* Bézier representation: + * + * @snippet easings.cpp quadraticIn + * + * @see @ref cubicIn(), @ref quarticIn(), @ref quinticIn() + */ + inline Float quadraticIn(Float t) { return t*t; } + + /** + * @brief Quadratic out + * + * @htmlinclude easings-quadraticout.svg + * + * @f[ + * y = 1 - (1 - x)^2 = (2 - x) x + * @f] + * + * *Exact* Bézier representation: + * + * @snippet easings.cpp quadraticOut + * + * @see @ref cubicOut(), @ref quarticOut(), @ref quinticOut() + */ + inline Float quadraticOut(Float t) { return -t*(t - 2.0f); } + + /** + * @brief Quadratic in and out + * + * Combination of @ref quadraticIn() and @ref quadraticOut(). + * + * @htmlinclude easings-quadraticinout.svg + * + * @f[ + * y = \begin{cases} + * 2 x^2, & x < 0.5 \\ + * 1 - 2 (1 - x)^2, & x \ge 0.5 + * \end{cases} + * @f] + * + * Approximate Bézier representation: + * + * @snippet easings.cpp quadraticInOut + * + * @see @ref cubicInOut(), @ref quarticInOut(), @ref quinticInOut() + */ + inline Float quadraticInOut(Float t) { + if(t < 0.5f) return 2.0f*t*t; + + const Float inv = 1.0f - t; + return 1.0f - 2.0f*inv*inv; + } -@f[ - y = \begin{cases} - 8 x^4, & x < 0.5 \\ - 1 - 8 (1 - x)^4, & x \ge 0.5 - \end{cases} -@f] + /** + * @brief Cubic in + * + * @htmlinclude easings-cubicin.svg + * + * @f[ + * y = x^3 + * @f] + * + * *Exact* Bézier representation: + * + * @snippet easings.cpp cubicIn + * + * @see @ref quadraticIn(), @ref quarticIn(), @ref quinticIn() + */ + inline Float cubicIn(Float t) { return t*t*t; } + + /** + * @brief Cubic out + * + * @htmlinclude easings-cubicout.svg + * + * @f[ + * y = 1 - (1 - x)^3 + * @f] + * + * *Exact* Bézier representation: + * + * @snippet easings.cpp cubicOut + * + * @see @ref quadraticOut(), @ref quarticOut(), @ref quinticOut() + */ + inline Float cubicOut(Float t) { + const Float inv = t - 1.0f; + return inv*inv*inv + 1.0f; + } -@see @ref quadraticInOut(), @ref cubicInOut(), @ref quinticInOut() -*/ -inline Float quarticInOut(Float t) { - /* Instead of t*t*t*t suggesting the optimization as described above */ + /** + * @brief Cubic in and out + * + * Combination of @ref cubicIn() and @ref cubicOut(). + * + * @htmlinclude easings-cubicinout.svg + * + * @f[ + * y = \begin{cases} + * 4 x^3, & x < 0.5 \\ + * 1 - 4 (1 - x)^3, & x \ge 0.5 + * \end{cases} + * @f] + * + * Approximate Bézier representation: + * + * @snippet easings.cpp cubicInOut + * + * @see @ref quadraticInOut(), @ref quarticInOut(), @ref quinticInOut() + */ + inline Float cubicInOut(Float t) { + if(t < 0.5f) return 4.0f*t*t*t; + + const Float inv = 1.0f - t; + return 1.0f - 4.0f*inv*inv*inv; + } - if(t < 0.5f) { + /** + * @brief Quartic in + * + * @htmlinclude easings-quarticin.svg + * + * @f[ + * y = x^4 + * @f] + * + * @see @ref quadraticIn(), @ref cubicIn(), @ref quinticIn() + */ + inline Float quarticIn(Float 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; - return 8*tt*tt; + return tt*tt; } - const Float inv = 1.0f - t; - const Float quad = inv*inv; - return 1.0f - 8.0f*quad*quad; -} - -/** -@brief Quintic in - -@htmlinclude easings-quinticin.svg - -@f[ - y = x^5 -@f] - -@see @ref quadraticIn(), @ref cubicIn(), @ref quarticIn() -*/ -inline Float quinticIn(Float t) { - /* Instead of t*t*t*t*t suggesting the optimization as described above */ - const Float tt = t*t; - return tt*t*tt; -} - -/** -@brief Quintic out - -@htmlinclude easings-quinticout.svg - -@f[ - y = 1 - (1 - x)^5 -@f] - -@see @ref quadraticOut(), @ref cubicOut(), @ref quarticOut() -*/ -inline Float quinticOut(Float 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; -} - -/** -@brief Quintic in and out - -Combination of @ref quinticIn() and @ref quinticOut(). - -@htmlinclude easings-quinticinout.svg - -@f[ - y = \begin{cases} - 16 x^5, & x < 0.5 \\ - 1 - 16 (1 - x)^5, & x \ge 0.5 - \end{cases} -@f] + /** + * @brief Quartic out + * + * @htmlinclude easings-quarticout.svg + * + * @f[ + * y = 1 - (1 - x)^4 + * @f] + * + * @see @ref quadraticOut(), @ref cubicOut(), @ref quinticOut() + */ + inline Float quarticOut(Float 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; + } -@see @ref quadraticInOut(), @ref cubicInOut(), @ref quarticInOut() -*/ -inline Float quinticInOut(Float t) { - /* Instead of t*t*t*t*t suggesting the optimization as described above */ + /** + * @brief Quartic in and out + * + * Combination of @ref quarticIn() and @ref quarticOut(). + * + * @htmlinclude easings-quarticinout.svg + * + * @f[ + * y = \begin{cases} + * 8 x^4, & x < 0.5 \\ + * 1 - 8 (1 - x)^4, & x \ge 0.5 + * \end{cases} + * @f] + * + * @see @ref quadraticInOut(), @ref cubicInOut(), @ref quinticInOut() + */ + inline Float quarticInOut(Float t) { + /* Instead of t*t*t*t suggesting the optimization as described above */ + + if(t < 0.5f) { + const Float 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; + } - if(t < 0.5f) { + /** + * @brief Quintic in + * + * @htmlinclude easings-quinticin.svg + * + * @f[ + * y = x^5 + * @f] + * + * @see @ref quadraticIn(), @ref cubicIn(), @ref quarticIn() + */ + inline Float quinticIn(Float t) { + /* Instead of t*t*t*t*t suggesting the optimization as described + above */ const Float tt = t*t; - return 16.0f*tt*t*tt; + return tt*t*tt; } - const Float inv = 1.0f - t; - const Float quad = inv*inv; - return 1.0f - 16.0f*quad*inv*quad; -} - -/** -@brief Sine in - -@htmlinclude easings-sinein.svg - -@f[ - y = 1 + \sin(\frac{\pi}{2} (x - 1)) -@f] - -@see @ref circularIn() -*/ -inline Float sineIn(Float t) { - return 1.0f + std::sin(Constants::piHalf()*(t - 1.0f)); -} - -/** -@brief Sine out - -@htmlinclude easings-sineout.svg - -@f[ - y = \sin(\frac{\pi}{2} x) -@f] - -@see @ref circularOut() -*/ -inline Float sineOut(Float t) { - return std::sin(Constants::piHalf()*t); -} - -/** -@brief Sine in and out - -Combination of @ref sineIn() and @ref sineOut(). - -@htmlinclude easings-sineinout.svg - -@f[ - y = \frac{1}{2} (1 - \cos(\pi x)) -@f] - -@see @ref circularInOut() -*/ -inline Float sineInOut(Float t) { - return 0.5f*(1.0f - std::cos(t*Constants::pi())); -} - -/** -@brief Circular in - -@htmlinclude easings-circularin.svg - -@f[ - y = 1 - \sqrt{1 - x^2} -@f] - -@see @ref sineIn() -*/ -inline Float circularIn(Float t) { - return 1.0f - std::sqrt(1.0f - t*t); -} - -/** -@brief Circular out - -@htmlinclude easings-circularout.svg - -@f[ - y = \sqrt{(2 - x) x} -@f] - -@see @ref sineOut() -*/ -inline Float circularOut(Float t) { - return std::sqrt((2.0f - t)*t); -} - -/** -@brief Circular in and out - -Combination of @ref circularIn() and @ref circularOut(). - -@htmlinclude easings-circularinout.svg - -@f[ - y = \begin{cases} - \frac{1}{2} (1 - \sqrt{1 - (2x)^2}), & x < 0.5 \\ - \frac{1}{2} (1 + \sqrt{1 - (2x - 2)^2}), & x \ge 0.5 - \end{cases} -@f] - -@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)); -} - -/** -@brief Exponential in - -Contrary to Robert Penner's book but consistently with other implementations -has a special case for @f$ x \le 0 @f$, because @f$ 2^{-10} = 0.0009765625 @f$ -otherwise. - -@htmlinclude easings-exponentialin.svg - -@f[ - y = \begin{cases} - 0, & x \le 0 \\ - 2^{10(x - 1)}, & x \ne 0 - \end{cases} -@f] - -@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)); -} - -/** -@brief Exponential out - -Contrary to Robert Penner's book but consistently with other implementations -has a special case for @f$ x \ge 1 @f$, because @f$ 2^{-10} = 0.0009765625 @f$ -otherwise. - -@htmlinclude easings-exponentialout.svg - -@f[ - y = \begin{cases} - 2^{-10 x}, & x < 1 \\ - 1, & x \ge 1 - \end{cases} -@f] - -@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); -} - -/** -@brief Exponential in and out - -Combination of @ref exponentialIn() and @ref exponentialOut(). Contrary to -Robert Penner's book but consistently with other implementations has a special -case for @f$ x \notin \{0, 1\} @f$, because @f$ 2^{-10} = 0.0009765625 @f$ -otherwise. - -@htmlinclude easings-exponentialinout.svg - -@f[ - y = \begin{cases} - 0, & x \le 0 \\ - \frac{1}{2} 2^{20 x - 10}, & x \in (0, 0.5) \\ - 1 - \frac{1}{2} 2^{10 - 20 x}, & x \in [0.5, 1) \\ - 1, & x \ge 1 - \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; -} - -/** -@brief Elastic in - -Combines @ref sineIn() and @ref exponentialIn(). - -@htmlinclude easings-elasticin.svg - -@f[ - 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); -} - -/** -@brief Elastic out - -Combines @ref sineOut() and @ref exponentialOut(). - -@htmlinclude easings-elasticout.svg - -@f[ - 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)); -} - -/** -@brief Elastic in and out - -Combination of @ref elasticIn() and @ref elasticOut() (or @ref sineInOut() and -@ref exponentialInOut()). - -@htmlinclude easings-elasticinout.svg - -@f[ - y = \begin{cases} - \frac{1}{2} 2^{10 (2x - 1)} \sin(13 \pi x), & x < 0.5 \\ - 1 - \frac{1}{2} 2^{10 (1 - 2x)} \sin(13 \pi x), & x \ge 0.5 - \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); -} + /** + * @brief Quintic out + * + * @htmlinclude easings-quinticout.svg + * + * @f[ + * y = 1 - (1 - x)^5 + * @f] + * + * @see @ref quadraticOut(), @ref cubicOut(), @ref quarticOut() + */ + inline Float quinticOut(Float 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; + } -/** -@brief Back in + /** + * @brief Quintic in and out + * + * Combination of @ref quinticIn() and @ref quinticOut(). + * + * @htmlinclude easings-quinticinout.svg + * + * @f[ + * y = \begin{cases} + * 16 x^5, & x < 0.5 \\ + * 1 - 16 (1 - x)^5, & x \ge 0.5 + * \end{cases} + * @f] + * + * @see @ref quadraticInOut(), @ref cubicInOut(), @ref quarticInOut() + */ + inline Float quinticInOut(Float 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; + } + + const Float inv = 1.0f - t; + const Float quad = inv*inv; + return 1.0f - 16.0f*quad*inv*quad; + } -@htmlinclude easings-backin.svg + /** + * @brief Sine in + * + * @htmlinclude easings-sinein.svg + * + * @f[ + * y = 1 + \sin(\frac{\pi}{2} (x - 1)) + * @f] + * + * @see @ref circularIn() + */ + inline Float sineIn(Float t) { + return 1.0f + std::sin(Constants::piHalf()*(t - 1.0f)); + } -@f[ - y = x^3 - x \sin(\pi x) -@f] -*/ -inline Float backIn(Float t) { - return t*(t*t - std::sin(Constants::pi()*t)); -} + /** + * @brief Sine out + * + * @htmlinclude easings-sineout.svg + * + * @f[ + * y = \sin(\frac{\pi}{2} x) + * @f] + * + * @see @ref circularOut() + */ + inline Float sineOut(Float t) { + return std::sin(Constants::piHalf()*t); + } -/** -@brief Back out + /** + * @brief Sine in and out + * + * Combination of @ref sineIn() and @ref sineOut(). + * + * @htmlinclude easings-sineinout.svg + * + * @f[ + * y = \frac{1}{2} (1 - \cos(\pi x)) + * @f] + * + * @see @ref circularInOut() + */ + inline Float sineInOut(Float t) { + return 0.5f*(1.0f - std::cos(t*Constants::pi())); + } -@htmlinclude easings-backout.svg + /** + * @brief Circular in + * + * @htmlinclude easings-circularin.svg + * + * @f[ + * y = 1 - \sqrt{1 - x^2} + * @f] + * + * @see @ref sineIn() + */ + inline Float circularIn(Float t) { + return 1.0f - std::sqrt(1.0f - t*t); + } -@f[ - 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)); -} + /** + * @brief Circular out + * + * @htmlinclude easings-circularout.svg + * + * @f[ + * y = \sqrt{(2 - x) x} + * @f] + * + * @see @ref sineOut() + */ + inline Float circularOut(Float t) { + return std::sqrt((2.0f - t)*t); + } -/** -@brief Back in and out + /** + * @brief Circular in and out + * + * Combination of @ref circularIn() and @ref circularOut(). + * + * @htmlinclude easings-circularinout.svg + * + * @f[ + * y = \begin{cases} + * \frac{1}{2} (1 - \sqrt{1 - (2x)^2}), & x < 0.5 \\ + * \frac{1}{2} (1 + \sqrt{1 - (2x - 2)^2}), & x \ge 0.5 + * \end{cases} + * @f] + * + * @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)); + } -Combination of @ref backIn() and @ref backOut(). + /** + * @brief Exponential in + * + * Contrary to Robert Penner's book but consistently with other + * implementations has a special case for @f$ x \le 0 @f$, because + * @f$ 2^{-10} = 0.0009765625 @f$ otherwise. + * + * @htmlinclude easings-exponentialin.svg + * + * @f[ + * y = \begin{cases} + * 0, & x \le 0 \\ + * 2^{10(x - 1)}, & x \ne 0 + * \end{cases} + * @f] + * + * @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)); + } -@htmlinclude easings-backinout.svg + /** + * @brief Exponential out + * + * Contrary to Robert Penner's book but consistently with other + * implementations has a special case for @f$ x \ge 1 @f$, because + * @f$ 2^{-10} = 0.0009765625 @f$ otherwise. + * + * @htmlinclude easings-exponentialout.svg + * + * @f[ + * y = \begin{cases} + * 2^{-10 x}, & x < 1 \\ + * 1, & x \ge 1 + * \end{cases} + * @f] + * + * @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); + } -@f[ - y = \begin{cases} - \frac{1}{2} ((2x)^3 - 2x \sin(2 \pi x)), & x < 0.5 \\ - 1 - \frac{1}{2} ((2 - 2x)^3 - (2 - 2x) \sin(\pi (2 - 2x)), & x \ge 0.5 - \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)); + /** + * @brief Exponential in and out + * + * Combination of @ref exponentialIn() and @ref exponentialOut(). Contrary + * to Robert Penner's book but consistently with other implementations has + * a special case for @f$ x \notin \{0, 1\} @f$, because + * @f$ 2^{-10} = 0.0009765625 @f$ otherwise. + * + * @htmlinclude easings-exponentialinout.svg + * + * @f[ + * y = \begin{cases} + * 0, & x \le 0 \\ + * \frac{1}{2} 2^{20 x - 10}, & x \in (0, 0.5) \\ + * 1 - \frac{1}{2} 2^{10 - 20 x}, & x \in [0.5, 1) \\ + * 1, & x \ge 1 + * \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; } - const Float inv = 2.0f - 2.0f*t; - return 1.0f - 0.5f*inv*(inv*inv - std::sin(Constants::pi()*inv)); -} + /** + * @brief Elastic in + * + * Combines @ref sineIn() and @ref exponentialIn(). + * + * @htmlinclude easings-elasticin.svg + * + * @f[ + * 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); + } -#ifndef DOXYGEN_GENERATING_OUTPUT -Float bounceOut(Float); -#endif + /** + * @brief Elastic out + * + * Combines @ref sineOut() and @ref exponentialOut(). + * + * @htmlinclude easings-elasticout.svg + * + * @f[ + * 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)); + } -/** -@brief Bounce in + /** + * @brief Elastic in and out + * + * Combination of @ref elasticIn() and @ref elasticOut() (or + * @ref sineInOut() and @ref exponentialInOut()). + * + * @htmlinclude easings-elasticinout.svg + * + * @f[ + * y = \begin{cases} + * \frac{1}{2} 2^{10 (2x - 1)} \sin(13 \pi x), & x < 0.5 \\ + * 1 - \frac{1}{2} 2^{10 (1 - 2x)} \sin(13 \pi x), & x \ge 0.5 + * \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); + } -@htmlinclude easings-bouncein.svg -*/ -inline Float bounceIn(Float t) { - return 1.0f - bounceOut(1.0f - t); -} + /** + * @brief Back in + * + * @htmlinclude easings-backin.svg + * + * @f[ + * y = x^3 - x \sin(\pi x) + * @f] + */ + inline Float backIn(Float t) { + return t*(t*t - std::sin(Constants::pi()*t)); + } -/** -@brief Bounce out + /** + * @brief Back out + * + * @htmlinclude easings-backout.svg + * + * @f[ + * 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)); + } -@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; -} + /** + * @brief Back in and out + * + * Combination of @ref backIn() and @ref backOut(). + * + * @htmlinclude easings-backinout.svg + * + * @f[ + * y = \begin{cases} + * \frac{1}{2} ((2x)^3 - 2x \sin(2 \pi x)), + * & x < 0.5 \\ + * 1 - \frac{1}{2} ((2 - 2x)^3 - (2 - 2x) \sin(\pi (2 - 2x)), + * & x \ge 0.5 + * \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)); + } + + const Float inv = 2.0f - 2.0f*t; + return 1.0f - 0.5f*inv*(inv*inv - std::sin(Constants::pi()*inv)); + } -/** -@brief Bounce in and out + #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); + } -Combination of @ref bounceIn() and @ref bounceOut(). + /** + * @brief Bounce out + * + * @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; + } -@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; + /** + * @brief Bounce in and out + * + * Combination of @ref bounceIn() and @ref bounceOut(). + * + * @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; + } } -}}} +}} #endif