using System;
using System.Collections.Generic;
using UnityEngine;
namespace OM
{
///
/// Provides a static library of mathematical easing functions based on Robert Penner's easing equations.
/// Includes standard easing types (Linear, Sine, Cubic, etc.) with In, Out, and InOut variations,
/// as well as Elastic, Back, and Bounce effects. Also provides PingPong versions of these functions.
/// Allows retrieval of function delegates and direct evaluation.
///
public static class EaseLibrary
{
// --- Constants used in easing calculations ---
private const float Pi = Mathf.PI;
private const float TwoPi = 2 * Mathf.PI;
private const float S = 1.70158f; // Constant used in Back easing functions (overshoot amount)
private const float BounceFactor = 7.5625f; // Constant factor used in Bounce calculations
private const float Divisor = 1 / 2.75f; // Constant divisor used in Bounce calculations
///
/// Dictionary mapping EasingFunction enum values to their corresponding static implementation methods.
/// Used internally by GetEasingFunction and Evaluate for efficient function lookup.
///
private static readonly Dictionary> EasingFunctions = new()
{
// Linear
{ EasingFunction.Linear, EaseLinear },
// Sine
{ EasingFunction.InSine, EaseInSine },
{ EasingFunction.OutSine, EaseOutSine },
{ EasingFunction.InOutSine, EaseInOutSine },
// Cubic
{ EasingFunction.InCubic, EaseInCubic },
{ EasingFunction.OutCubic, EaseOutCubic },
{ EasingFunction.InOutCubic, EaseInOutCubic },
// Quint
{ EasingFunction.InQuint, EaseInQuint },
{ EasingFunction.OutQuint, EaseOutQuint },
{ EasingFunction.InOutQuint, EaseInOutQuint },
// Circ
{ EasingFunction.InCirc, EaseInCirc },
{ EasingFunction.OutCirc, EaseOutCirc },
{ EasingFunction.InOutCirc, EaseInOutCirc },
// Elastic
{ EasingFunction.InElastic, EaseInElastic },
{ EasingFunction.OutElastic, EaseOutElastic },
{ EasingFunction.InOutElastic, EaseInOutElastic },
// Quart
{ EasingFunction.InQuart, EaseInQuart },
{ EasingFunction.OutQuart, EaseOutQuart },
{ EasingFunction.InOutQuart, EaseInOutQuart },
// Expo
{ EasingFunction.InExpo, EaseInExpo },
{ EasingFunction.OutExpo, EaseOutExpo },
{ EasingFunction.InOutExpo, EaseInOutExpo },
// Quad
{ EasingFunction.InQuad, EaseInQuad },
{ EasingFunction.OutQuad, EaseOutQuad },
{ EasingFunction.InOutQuad, EaseInOutQuad },
// Back
{ EasingFunction.InBack, EaseInBack },
{ EasingFunction.OutBack, EaseOutBack },
{ EasingFunction.InOutBack, EaseInOutBack },
// Bounce
{ EasingFunction.InBounce, EaseInBounce },
{ EasingFunction.OutBounce, EaseOutBounce },
{ EasingFunction.InOutBounce, EaseInOutBounce },
// --- PingPong Versions ---
{ EasingFunction.LinearPingPong, EaseLinearPingPong },
{ EasingFunction.InSinePingPong, EaseInSinePingPong },
{ EasingFunction.OutSinePingPong, EaseOutSinePingPong },
{ EasingFunction.InOutSinePingPong, EaseInOutSinePingPong },
{ EasingFunction.InCubicPingPong, EaseInCubicPingPong },
{ EasingFunction.OutCubicPingPong, EaseOutCubicPingPong },
{ EasingFunction.InOutCubicPingPong, EaseInOutCubicPingPong },
{ EasingFunction.InQuintPingPong, EaseInQuintPingPong },
{ EasingFunction.OutQuintPingPong, EaseOutQuintPingPong },
{ EasingFunction.InOutQuintPingPong, EaseInOutQuintPingPong },
{ EasingFunction.InCircPingPong, EaseInCircPingPong },
{ EasingFunction.OutCircPingPong, EaseOutCircPingPong },
{ EasingFunction.InOutCircPingPong, EaseInOutCircPingPong },
{ EasingFunction.InElasticPingPong, EaseInElasticPingPong },
{ EasingFunction.OutElasticPingPong, EaseOutElasticPingPong },
{ EasingFunction.InOutElasticPingPong, EaseInOutElasticPingPong },
{ EasingFunction.InQuartPingPong, EaseInQuartPingPong },
{ EasingFunction.OutQuartPingPong, EaseOutQuartPingPong },
{ EasingFunction.InOutQuartPingPong, EaseInOutQuartPingPong },
{ EasingFunction.InExpoPingPong, EaseInExpoPingPong },
{ EasingFunction.OutExpoPingPong, EaseOutExpoPingPong },
{ EasingFunction.InOutExpoPingPong, EaseInOutExpoPingPong },
{ EasingFunction.InQuadPingPong, EaseInQuadPingPong },
{ EasingFunction.OutQuadPingPong, EaseOutQuadPingPong },
{ EasingFunction.InOutQuadPingPong, EaseInOutQuadPingPong },
{ EasingFunction.InBackPingPong, EaseInBackPingPong },
{ EasingFunction.OutBackPingPong, EaseOutBackPingPong },
{ EasingFunction.InOutBackPingPong, EaseInOutBackPingPong },
{ EasingFunction.InBouncePingPong, EaseInBouncePingPong },
{ EasingFunction.OutBouncePingPong, EaseOutBouncePingPong },
{ EasingFunction.InOutBouncePingPong, EaseInOutBouncePingPong }
};
///
/// Gets the delegate (Func) for the specified easing function type.
///
/// The enum value representing the desired easing function.
/// A Func delegate that takes a float (time) and returns a float (eased value). Returns EaseLinear if the specified type is not found.
public static Func GetEasingFunction(EasingFunction easingFunction)
{
// Try to retrieve the function from the dictionary. If not found, default to Linear.
return EasingFunctions.TryGetValue(easingFunction, out var easingFunc) ? easingFunc : EaseLinear;
}
///
/// Evaluates the specified easing function at time 't'.
///
/// The input time/progress, typically clamped between 0.0 and 1.0 before calling this.
/// The enum value representing the desired easing function.
/// The calculated eased value corresponding to the input time 't'.
public static float Evaluate(float t, EasingFunction easingFunction)
{
// Get the appropriate function delegate and invoke it with time 't'.
var easingFunc = GetEasingFunction(easingFunction);
return easingFunc(t);
}
// --- Easing Function Implementations ---
/// Linear easing (no acceleration).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseLinear(float t) => Mathf.LerpUnclamped(0, 1, t); // Note: LerpUnclamped used for consistency, though Lerp would work for t in [0,1].
/// Ease-in sine (starts slow, accelerates).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInSine(float t) => 1 - Mathf.Cos((t * Pi) / 2);
/// Ease-out sine (starts fast, decelerates).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutSine(float t) => Mathf.Sin((t * Pi) / 2);
/// Ease-in-out sine (slow start and end, fast middle).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutSine(float t) => -0.5f * (Mathf.Cos(Pi * t) - 1);
/// Ease-in cubic (starts slow, accelerates faster than sine).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInCubic(float t) => t * t * t;
/// Ease-out cubic (starts fast, decelerates faster than sine).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutCubic(float t) => 1 - Mathf.Pow(1 - t, 3);
/// Ease-in-out cubic (slow start and end, fast middle).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutCubic(float t) => t < 0.5f ? 4 * t * t * t : 1 - Mathf.Pow(-2 * t + 2, 3) / 2;
/// Ease-in quintic (starts very slow, accelerates sharply).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInQuint(float t) => t * t * t * t * t;
/// Ease-out quintic (starts very fast, decelerates sharply).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutQuint(float t) => 1 - Mathf.Pow(1 - t, 5);
/// Ease-in-out quintic (very slow start and end, sharp acceleration/deceleration).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutQuint(float t) => t < 0.5f ? 16 * t * t * t * t * t : 1 - Mathf.Pow(-2 * t + 2, 5) / 2;
/// Ease-in circular (starts slow, accelerates based on a circular path).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInCirc(float t) => 1 - Mathf.Sqrt(1 - t * t);
/// Ease-out circular (starts fast, decelerates based on a circular path).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutCirc(float t) => Mathf.Sqrt(1 - (t - 1) * (t - 1));
/// Ease-in-out circular (slow start and end, circular acceleration/deceleration).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutCirc(float t) => t < 0.5f
? (1 - Mathf.Sqrt(1 - 4 * t * t)) / 2
: (Mathf.Sqrt(1 - (-2 * t + 2) * (-2 * t + 2)) + 1) / 2;
/// Ease-in elastic (starts with an elastic "snap" inward).
/// Input time (0 to 1). Eased value (can go outside 0-1 range).
public static float EaseInElastic(float t) =>
t == 0 || t == 1 ? t : -Mathf.Pow(2, 10 * t - 10) * Mathf.Sin((t * 10 - 10.75f) * TwoPi / 3);
/// Ease-out elastic (ends with an elastic "snap" outward).
/// Input time (0 to 1). Eased value (can go outside 0-1 range).
public static float EaseOutElastic(float t) =>
t == 0 || t == 1 ? t : Mathf.Pow(2, -10 * t) * Mathf.Sin((t * 10 - 0.75f) * TwoPi / 3) + 1;
/// Ease-in-out elastic (combines inward and outward elastic snaps).
/// Input time (0 to 1). Eased value (can go outside 0-1 range).
public static float EaseInOutElastic(float t)
{
if (t == 0 || t == 1) return t;
t *= 2; // Scale t to 0-2 range for the combined calculation
return t < 1
? -0.5f * Mathf.Pow(2, 10 * t - 10) * Mathf.Sin((t * 10 - 10.75f) * TwoPi / 3) // In part
: Mathf.Pow(2, -10 * t + 10) * Mathf.Sin((t * 10 - 10.75f) * TwoPi / 3) * 0.5f + 1; // Out part (adjusted from original for symmetry)
}
/// Ease-in quartic (t^4, similar to cubic but steeper).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInQuart(float t) => t * t * t * t;
/// Ease-out quartic (similar to cubic but steeper).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutQuart(float t) => 1 - Mathf.Pow(1 - t, 4);
/// Ease-in-out quartic (similar to cubic but steeper).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutQuart(float t) => t < 0.5f ? 8 * t * t * t * t : 1 - Mathf.Pow(-2 * t + 2, 4) / 2;
/// Ease-in exponential (starts very slow, accelerates dramatically).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInExpo(float t) => t == 0 ? 0 : Mathf.Pow(2, 10 * t - 10);
/// Ease-out exponential (starts very fast, decelerates dramatically).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutExpo(float t) => t == 1 ? 1 : 1 - Mathf.Pow(2, -10 * t);
/// Ease-in-out exponential (dramatic acceleration and deceleration).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutExpo(float t)
{
if (t == 0) return 0;
if (t == 1) return 1;
t *= 2; // Scale t to 0-2 range
return t < 1 ? Mathf.Pow(2, 10 * t - 10) / 2 : (2 - Mathf.Pow(2, -10 * t + 10)) / 2;
}
/// Ease-in quadratic (t^2).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInQuad(float t) => t * t;
/// Ease-out quadratic.
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutQuad(float t) => 1 - (1 - t) * (1 - t);
/// Ease-in-out quadratic.
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutQuad(float t) => t < 0.5f ? 2 * t * t : 1 - Mathf.Pow(-2 * t + 2, 2) / 2;
/// Ease-in back (starts by moving slightly backward before moving forward).
/// Input time (0 to 1). Eased value (can go below 0).
public static float EaseInBack(float t) => t * t * ((S + 1) * t - S);
/// Ease-out back (overshoots the end slightly before settling).
/// Input time (0 to 1). Eased value (can go above 1).
public static float EaseOutBack(float t)
{
const float overshoot = S; // Use defined constant
t -= 1f;
return 1f + t * t * ((overshoot + 1f) * t + overshoot);
}
/// Ease-in-out back (combines the backward start and overshoot end).
/// Input time (0 to 1). Eased value (can go outside 0-1).
public static float EaseInOutBack(float t)
{
const float overshoot = S * 1.525f; // Adjusted overshoot for InOut
t *= 2;
if (t < 1)
{
return 0.5f * (t * t * ((overshoot + 1) * t - overshoot));
}
t -= 2;
return 0.5f * (t * t * ((overshoot + 1) * t + overshoot) + 2);
}
/// Ease-in bounce (simulates dropping onto the start point).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInBounce(float t) => 1 - EaseOutBounce(1 - t); // In-bounce is the reverse of out-bounce
/// Ease-out bounce (simulates bouncing to a stop at the end point).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseOutBounce(float t)
{
// Piecewise function simulating bounces
if (t < Divisor) return BounceFactor * t * t;
if (t < 2 * Divisor) return BounceFactor * (t -= 1.5f * Divisor) * t + 0.75f;
if (t < 2.5f * Divisor) return BounceFactor * (t -= 2.25f * Divisor) * t + 0.9375f;
return BounceFactor * (t -= 2.625f * Divisor) * t + 0.984375f;
}
/// Ease-in-out bounce (combines in and out bounce effects).
/// Input time (0 to 1). Eased value (0 to 1).
public static float EaseInOutBounce(float t) =>
t < 0.5f ? EaseInBounce(t * 2) * 0.5f : EaseOutBounce(t * 2 - 1) * 0.5f + 0.5f;
// --- Ping Pong Versions ---
/// Helper function to transform time 't' (0 to 1) into a ping-pong loop (0 -> 1 -> 0).
/// Input time (0 to 1). Ping-ponged time (0 to 1).
private static float PingPong(float t)
{
//return Mathf.PingPong(t * 2, 1); // Alternative implementation
// Map t from [0, 1] to [0, 2], then PingPong maps [0, 2] to [0, 1, 0]
t *= 2;
return (t <= 1) ? t : 2 - t;
}
/// Linear easing applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path.
public static float EaseLinearPingPong(float t) => EaseLinear(PingPong(t));
/// Ease-in sine applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with sine-in easing.
public static float EaseInSinePingPong(float t) => EaseInSine(PingPong(t));
/// Ease-out sine applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with sine-out easing.
public static float EaseOutSinePingPong(float t) => EaseOutSine(PingPong(t));
/// Ease-in-out sine applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with sine-in-out easing.
public static float EaseInOutSinePingPong(float t) => EaseInOutSine(PingPong(t));
/// Ease-in cubic applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with cubic-in easing.
public static float EaseInCubicPingPong(float t) => EaseInCubic(PingPong(t));
/// Ease-out cubic applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with cubic-out easing.
public static float EaseOutCubicPingPong(float t) => EaseOutCubic(PingPong(t));
/// Ease-in-out cubic applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with cubic-in-out easing.
public static float EaseInOutCubicPingPong(float t) => EaseInOutCubic(PingPong(t));
/// Ease-in quint applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quint-in easing.
public static float EaseInQuintPingPong(float t) => EaseInQuint(PingPong(t));
/// Ease-out quint applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quint-out easing.
public static float EaseOutQuintPingPong(float t) => EaseOutQuint(PingPong(t));
/// Ease-in-out quint applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quint-in-out easing.
public static float EaseInOutQuintPingPong(float t) => EaseInOutQuint(PingPong(t));
/// Ease-in circ applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with circ-in easing.
public static float EaseInCircPingPong(float t) => EaseInCirc(PingPong(t));
/// Ease-out circ applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with circ-out easing.
public static float EaseOutCircPingPong(float t) => EaseOutCirc(PingPong(t));
/// Ease-in-out circ applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with circ-in-out easing.
public static float EaseInOutCircPingPong(float t) => EaseInOutCirc(PingPong(t));
/// Ease-in elastic applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with elastic-in easing.
public static float EaseInElasticPingPong(float t) => EaseInElastic(PingPong(t));
/// Ease-out elastic applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with elastic-out easing.
public static float EaseOutElasticPingPong(float t) => EaseOutElastic(PingPong(t));
/// Ease-in-out elastic applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with elastic-in-out easing.
public static float EaseInOutElasticPingPong(float t) => EaseInOutElastic(PingPong(t));
/// Ease-in quart applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quart-in easing.
public static float EaseInQuartPingPong(float t) => EaseInQuart(PingPong(t));
/// Ease-out quart applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quart-out easing.
public static float EaseOutQuartPingPong(float t) => EaseOutQuart(PingPong(t));
/// Ease-in-out quart applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quart-in-out easing.
public static float EaseInOutQuartPingPong(float t) => EaseInOutQuart(PingPong(t));
/// Ease-in expo applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with expo-in easing.
public static float EaseInExpoPingPong(float t) => EaseInExpo(PingPong(t));
/// Ease-out expo applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with expo-out easing.
public static float EaseOutExpoPingPong(float t) => EaseOutExpo(PingPong(t));
/// Ease-in-out expo applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with expo-in-out easing.
public static float EaseInOutExpoPingPong(float t) => EaseInOutExpo(PingPong(t));
/// Ease-in quad applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quad-in easing.
public static float EaseInQuadPingPong(float t) => EaseInQuad(PingPong(t));
/// Ease-out quad applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quad-out easing.
public static float EaseOutQuadPingPong(float t) => EaseOutQuad(PingPong(t));
/// Ease-in-out quad applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with quad-in-out easing.
public static float EaseInOutQuadPingPong(float t) => EaseInOutQuad(PingPong(t));
/// Ease-in back applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with back-in easing.
public static float EaseInBackPingPong(float t) => EaseInBack(PingPong(t));
/// Ease-out back applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with back-out easing.
public static float EaseOutBackPingPong(float t) => EaseOutBack(PingPong(t));
/// Ease-in-out back applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with back-in-out easing.
public static float EaseInOutBackPingPong(float t) => EaseInOutBack(PingPong(t));
/// Ease-in bounce applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with bounce-in easing.
public static float EaseInBouncePingPong(float t) => EaseInBounce(PingPong(t));
/// Ease-out bounce applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with bounce-out easing.
public static float EaseOutBouncePingPong(float t) => EaseOutBounce(PingPong(t));
/// Ease-in-out bounce applied over a ping-pong time transformation.
/// Input time (0 to 1). Eased value following a 0->1->0 path with bounce-in-out easing.
public static float EaseInOutBouncePingPong(float t) => EaseInOutBounce(PingPong(t));
}
}