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)); } }