imj-base-0.1.0.2: src/Imj/Graphics/Math/Ease.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Imj.Graphics.Math.Ease
(
-- * 4th order /inverse/ easing, continuous
{- | Easing is traditionally seen as a function from /time/ to value.
Here, it is a function from /value/ to time, hence the use of the term /Inverse/ in the title.
-}
invQuartEaseInOut
-- * From continuous to discrete
{- |
Easing in a continuous world is /easy/ (no pun intended), but easing in a
discrete world is harder : we have to make sure the discretization will
not break the visual easing effect.
The 'discreteAdaptor' function does just that, making a continuous easing
function usable in a discrete context.
-}
, discreteAdaptor
-- * 4th order inverse easing, discrete
-- | Using 'discreteAdaptor' on 'invQuartEaseInOut' we can make
-- 'discreteInvQuartEaseInOut' :
, discreteInvQuartEaseInOut
) where
import Imj.Prelude
-- cf. this for formatting : https://math.meta.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference
{- |
Returns the time \( t \in [\,0,1]\, \) at which a value \( y \in [\,0,1]\,\) is reached
given a <http://gizma.com/easing/ 4th order ease in-out function> \( quartEaseInOut \):
\[ y = quartEaseInOut(t) =
\begin{cases}
{1 \over 2} * (2*t)^4, & \;\;\;\; \text{if $t < {1 \over 2}$} \\[2ex]
-{1 \over 2} * \left( [ 2*(t-1) ]^4 - 2 \right), & \;\;\;\; \text{if $t > {1 \over 2}$}
\end{cases}
\]
To find the formulas of 'invQuartEaseInOut', we need to invert \( quartEaseInOut \),
i.e. we need to express \(t\) in terms of \(y\):
\[ \text{$quartEaseInOut$ is strictly increasing} \implies
\begin{cases}
t<{1 \over 2} \iff y<{1 \over 2} \\
t>{1 \over 2} \iff y>{1 \over 2}
\end{cases}
\]
\[ \begin{alignedat}{3}
\text{if $y < {1 \over 2} $, given the $quartEaseInOut$ equation for $t < {1 \over 2} $ :}
&& y &= {1 \over 2} * (2*t)^4 && \\
\implies && \quad t &= \left({y \over 2^3}\right)^{1/4} && \quad \forall y < {1 \over 2} \\
\text{if $y > {1 \over 2} $, given the $quartEaseInOut$ equation for $t > {1 \over 2} $ :}
&& y &= - {1 \over 2} * \left( [2*(t-1)]^4 - 2 \right) && \\
\implies && \quad t &= 1-\left[{1-y \over 2^3}\right]^{1/4} && \quad \forall y > {1 \over 2}
\end{alignedat} \]
/Note that there are multiple solutions, we chose the ones that produce results in the \( [\,0,1]\, \) range./
Hence, the formulas for 'invQuartEaseInOut' are :
\[ t = invQuartEaseInOut(y) =
\begin{cases}
\left({y \over 2^3}\right)^{1/4}, & \text{if $y < {1 \over 2}$} \\[2ex]
1-\left[{1-y \over 2^3}\right]^{1/4}, & \text{if $y > {1 \over 2}$}
\end{cases}
\]
-}
invQuartEaseInOut :: Float
-- ^ Value : \( y \)
-> Float
-- ^ Time : \( t \)
invQuartEaseInOut y =
if y < 0.5
then
(y / 8.0) ** (1.0/4.0)
else
1.0 - ((1.0 - y) / 8.0) ** (1.0/4.0)
-- | Adapts continuous inout ease functions to the discrete case.
discreteAdaptor :: (Float -> Float)
-- ^ Continuous (optionally inverse) ease in/out function
-> Int
-- ^ The number of discrete steps
-> Float
-- ^ Input value
-> Float
-- ^ (optionnaly inverse) Eased value
discreteAdaptor f n v =
-- We use the center of the intervals instead of the extremities.
let nIntervals = n
intervalSize = recip $ fromIntegral nIntervals
firstValue = intervalSize / 2
lastValue = 1 - firstValue
scaledValue = firstValue + v * (lastValue - firstValue)
in f scaledValue
-- | Returns the time (in range [0 1]) at which a value (in range [0 1]) is reached
-- given a 4th order ease in-out function, and a total number of discrete steps.
discreteInvQuartEaseInOut :: Int
-- ^ The number of discrete steps
-> Float
-- ^ Value
-> Float
-- ^ Time
discreteInvQuartEaseInOut = discreteAdaptor invQuartEaseInOut