/*
 * +================================================================================+
 *      __  ___  ______________  __  __  ______________   ____
 *     / / / / |/ /  _/_  __/\ \/ / / / / /_  __/  _/ /  / __/
 *    / /_/ /    // /  / /    \  / / /_/ / / / _/ // /___\ \
 *    \____/_/|_/___/ /_/     /_/  \____/ /_/ /___/____/___/
 *
 * +================================================================================+
 *
 *    "THE BEER-WARE LICENSE"
 *    -----------------------
 *
 *    As long as you retain this notice you can do whatever you want with this
 *    stuff. However it is expressly forbidden to sell or commercially distribute
 *    Unity Utils or parts of it apart from your games.
 *    If you meet any of the authors some day, and you think this stuff is
 *    worth it, you can buy them a beer in return.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *    THE SOFTWARE.
 *
 * +================================================================================+
 *
 *    Authors:
 *    David Krummenacher (dave@timbookedtwo.com)
 *
 */

using UnityEngine;
using Utils.Helpers;

namespace Utils {

	/// <summary>
	/// Easing functions.
	/// </summary>
	public abstract class Ease {

		#region Animation curve based

		/// <summary>
		/// Interpolates between two float values with easing.
		/// </summary>
		/// <returns>The eased interpolated float value.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float GetEase(AnimationCurve curve, float t, float from = 0f, float to = 1) {
			return from + (to - from) * curve.Evaluate(t);
		}

		/// <summary>
		/// Interpolates between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector2 GetEase(AnimationCurve curve, float t, Vector2 from, Vector2 to) {
			return VectorHelper.LerpWithOvershoot(from, to, GetEase(curve, t));
		}

		/// <summary>
		/// Interpolates between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector3 GetEase(AnimationCurve curve, float t, Vector3 from, Vector3 to) {
			return VectorHelper.LerpWithOvershoot(from, to, GetEase(curve, t));
		}

		/// <summary>
		/// Interpolates between two quaternions with easing.
		/// </summary>
		/// <returns>The eased interpolated quaternion.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from quaternion; 1 = to quaternion</param>
		/// <param name="from">The from quaternion.</param>
		/// <param name="to">The to quaternion.</param>
		public static Quaternion GetEase(AnimationCurve curve, float t, Quaternion from, Quaternion to) {
			return QuaternionHelper.SlerpWithOvershoot(from, to, GetEase(curve, t));
		}

		/// <summary>
		/// Interpolates back and forth between two float values with easing.
		/// </summary>
		/// <returns>The eased interpolated float value.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float GetPingPong(AnimationCurve curve, float t, float from = 0f, float to = 1) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(curve, t - Mathf.Floor(t), from, to);
			else return GetEase(curve, t - Mathf.Floor(t), to, from);
		}

		/// <summary>
		/// Interpolates back and forth between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector2 GetPingPong(AnimationCurve curve, float t, Vector2 from, Vector2 to) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(curve, t - Mathf.Floor(t), from, to);
			else return GetEase(curve, t - Mathf.Floor(t), to, from);
		}

		/// <summary>
		/// Interpolates back and forth between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector3 GetPingPong(AnimationCurve curve, float t, Vector3 from, Vector3 to) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(curve, t - Mathf.Floor(t), from, to);
			else return GetEase(curve, t - Mathf.Floor(t), to, from);
		}

		/// <summary>
		/// Interpolates back and forth between two quaternions with easing.
		/// </summary>
		/// <returns>The eased interpolated quaternion.</returns>
		/// <param name="curve">The animation curve used for easing.</param>
		/// <param name="t">The fraction. 0 = from quaternion; 1 = to quaternion</param>
		/// <param name="from">The from quaternion.</param>
		/// <param name="to">The to quaternion.</param>
		public static Quaternion GetPingPong(AnimationCurve curve, float t, Quaternion from, Quaternion to) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(curve, t - Mathf.Floor(t), from, to);
			else return GetEase(curve, t - Mathf.Floor(t), to, from);
		}

		#endregion

		#region Equation based

		/// <summary>
		/// All ease functions available.
		/// </summary>
		public enum EaseFunctions {
			/// <summary>
			/// Linear interpolation.
			/// </summary>
			Linear,
			/// <summary>
			/// Hermite interpolation that eases in and out.
			/// </summary>
			Hermite,
			/// <summary>
			/// Boing interpolation that bounces once at the end.
			/// </summary>
			Boing,
			/// <summary>
			/// Stationary bounce interpolation that bounces in the same position. Similar to the bouncing dock icons in osx.
			/// </summary>
			StationaryBounce,
			/// <summary>
			/// Quadratic interpolation that eases out.
			/// </summary>
			QuadraticEaseOut,
			/// <summary>
			/// Quadratic interpolation that eases in.
			/// </summary>
			QuadraticEaseIn,
			/// <summary>
			/// Quadratic interpolation that eases in and out.
			/// </summary>
			QuadraticEaseInOut,
			/// <summary>
			/// Quadratic interpolation that eases in the middle.
			/// </summary>
			QuadraticEaseOutIn,
			/// <summary>
			/// Exponential interpolation that eases out.
			/// </summary>
			ExponentialEaseOut,
			/// <summary>
			/// Exponential interpolation that eases in.
			/// </summary>
			ExponentialEaseIn,
			/// <summary>
			/// Exponential interpolation that eases in and out.
			/// </summary>
			ExponentialEaseInOut,
			/// <summary>
			/// Exponential interpolation that eases in the middle.
			/// </summary>
			ExponentialEaseOutIn,
			/// <summary>
			/// Cubic interpolation that eases out.
			/// </summary>
			CubicEaseOut,
			/// <summary>
			/// Cubic interpolation that eases in.
			/// </summary>
			CubicEaseIn,
			/// <summary>
			/// Cubic interpolation that eases in and out.
			/// </summary>
			CubicEaseInOut,
			/// <summary>
			/// Cubic interpolation that eases in the middle.
			/// </summary>
			CubicEaseOutIn,
			/// <summary>
			/// Quartic interpolation that eases out.
			/// </summary>
			QuarticEaseOut,
			/// <summary>
			/// Quartic interpolation that eases in.
			/// </summary>
			QuarticEaseIn,
			/// <summary>
			/// Quartic interpolation that eases in and out.
			/// </summary>
			QuarticEaseInOut,
			/// <summary>
			/// Quartic interpolation that eases in the middle.
			/// </summary>
			QuarticEaseOutIn,
			/// <summary>
			/// Quintic interpolation that eases out.
			/// </summary>
			QuinticEaseOut,
			/// <summary>
			/// Quintic interpolation that eases in.
			/// </summary>
			QuinticEaseIn,
			/// <summary>
			/// Quintic interpolation that eases in and out.
			/// </summary>
			QuinticEaseInOut,
			/// <summary>
			/// Quintic interpolation that eases in the middle.
			/// </summary>
			QuinticEaseOutIn,
			/// <summary>
			/// Circular interpolation that eases out.
			/// </summary>
			CircularEaseOut,
			/// <summary>
			/// Circular interpolation that eases in.
			/// </summary>
			CircularEaseIn,
			/// <summary>
			/// Circular interpolation that eases in and out.
			/// </summary>
			CircularEaseInOut,
			/// <summary>
			/// Circular interpolation that eases in the middle.
			/// </summary>
			CircularEaseOutIn,
			/// <summary>
			/// Sine interpolation that eases out.
			/// </summary>
			SineEaseOut,
			/// <summary>
			/// Sine interpolation that eases in.
			/// </summary>
			SineEaseIn,
			/// <summary>
			/// Sine interpolation that eases in and out.
			/// </summary>
			SineEaseInOut,
			/// <summary>
			/// Sine interpolation that eases in the middle.
			/// </summary>
			SineEaseOutIn,
			/// <summary>
			/// Elastic interpolation that eases out. It bounces and overshoots at the end.
			/// </summary>
			ElasticEaseOut,
			/// <summary>
			/// Elastic interpolation that eases in. It bounces and overshoots in the beginning.
			/// </summary>
			ElasticEaseIn,
			/// <summary>
			/// Elastic interpolation that eases in and out. It bounces and overshoots in the beginning and at the end.
			/// </summary>
			ElasticEaseInOut,
			/// <summary>
			/// Elastic interpolation that eases in the middle. It bounces and overshoots in the middle.
			/// </summary>
			ElasticEaseOutIn,
			/// <summary>
			/// Bounce interpolation that eases out. It bounces but doesn't overshoot at the end.
			/// </summary>
			BounceEaseOut,
			/// <summary>
			/// Bounce interpolation that eases in. It bounces but doesn't overshoot in the beginning.
			/// </summary>
			BounceEaseIn,
			/// <summary>
			/// Bounce interpolation that eases in and out. It bounces but doesn't overshoot in the beginning and at the end.
			/// </summary>
			BounceEaseInOut,
			/// <summary>
			/// Bounce interpolation that eases in the middle. It bounces but doesn't overshoot in the middle.
			/// </summary>
			BounceEaseOutIn,
			/// <summary>
			/// Back interpolation that eases out. It overshoots once at the end.
			/// </summary>
			BackEaseOut,
			/// <summary>
			/// Back interpolation that eases in. It overshoots once in the beginning.
			/// </summary>
			BackEaseIn,
			/// <summary>
			/// Back interpolation that eases in and out. It overshoots once in the beginning and once at the end.
			/// </summary>
			BackEaseInOut,
			/// <summary>
			/// Back interpolation that eases in the middle. It overshoots in both directions in the middle.
			/// </summary>
			BackEaseOutIn
		}

		/// <summary>
		/// Interpolates between two float values with easing.
		/// </summary>
		/// <returns>The eased interpolated float value.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float GetEase(EaseFunctions function, float t, float from = 0f, float to = 1) {
			switch (function) {
				case EaseFunctions.Linear: return Linear(t, from, to);
				case EaseFunctions.Hermite: return Hermite(t, from, to);
				case EaseFunctions.Boing: return Boing(t, from, to);
				case EaseFunctions.StationaryBounce: return StationaryBounce(t, from, to);
				case EaseFunctions.QuadraticEaseOut: return QuadraticEaseOut(t, from, to);
				case EaseFunctions.QuadraticEaseIn: return QuadraticEaseIn(t, from, to);
				case EaseFunctions.QuadraticEaseInOut: return QuadraticEaseInOut(t, from, to);
				case EaseFunctions.QuadraticEaseOutIn: return QuadraticEaseOutIn(t, from, to);
				case EaseFunctions.ExponentialEaseOut: return ExponentialEaseOut(t, from, to);
				case EaseFunctions.ExponentialEaseIn: return ExponentialEaseIn(t, from, to);
				case EaseFunctions.ExponentialEaseInOut: return ExponentialEaseInOut(t, from, to);
				case EaseFunctions.ExponentialEaseOutIn: return ExponentialEaseOutIn(t, from, to);
				case EaseFunctions.CubicEaseOut: return CubicEaseOut(t, from, to);
				case EaseFunctions.CubicEaseIn: return CubicEaseIn(t, from, to);
				case EaseFunctions.CubicEaseInOut: return CubicEaseInOut(t, from, to);
				case EaseFunctions.CubicEaseOutIn: return CubicEaseOutIn(t, from, to);
				case EaseFunctions.QuarticEaseOut: return QuarticEaseOut(t, from, to);
				case EaseFunctions.QuarticEaseIn: return QuarticEaseIn(t, from, to);
				case EaseFunctions.QuarticEaseInOut: return QuarticEaseInOut(t, from, to);
				case EaseFunctions.QuarticEaseOutIn: return QuarticEaseOutIn(t, from, to);
				case EaseFunctions.QuinticEaseOut: return QuinticEaseOut(t, from, to);
				case EaseFunctions.QuinticEaseIn: return QuinticEaseIn(t, from, to);
				case EaseFunctions.QuinticEaseInOut: return QuinticEaseInOut(t, from, to);
				case EaseFunctions.QuinticEaseOutIn: return QuinticEaseOutIn(t, from, to);
				case EaseFunctions.CircularEaseOut: return CircularEaseOut(t, from, to);
				case EaseFunctions.CircularEaseIn: return CircularEaseIn(t, from, to);
				case EaseFunctions.CircularEaseInOut: return CircularEaseInOut(t, from, to);
				case EaseFunctions.CircularEaseOutIn: return CircularEaseOutIn(t, from, to);
				case EaseFunctions.SineEaseOut: return SineEaseOut(t, from, to);
				case EaseFunctions.SineEaseIn: return SineEaseIn(t, from, to);
				case EaseFunctions.SineEaseInOut: return SineEaseInOut(t, from, to);
				case EaseFunctions.SineEaseOutIn: return SineEaseOutIn(t, from, to);
				case EaseFunctions.ElasticEaseOut: return ElasticEaseOut(t, from, to);
				case EaseFunctions.ElasticEaseIn: return ElasticEaseIn(t, from, to);
				case EaseFunctions.ElasticEaseInOut: return ElasticEaseInOut(t, from, to);
				case EaseFunctions.ElasticEaseOutIn: return ElasticEaseOutIn(t, from, to);
				case EaseFunctions.BounceEaseOut: return BounceEaseOut(t, from, to);
				case EaseFunctions.BounceEaseIn: return BounceEaseIn(t, from, to);
				case EaseFunctions.BounceEaseInOut: return BounceEaseInOut(t, from, to);
				case EaseFunctions.BounceEaseOutIn: return BounceEaseOutIn(t, from, to);
				case EaseFunctions.BackEaseOut: return BackEaseOut(t, from, to);
				case EaseFunctions.BackEaseIn: return BackEaseIn(t, from, to);
				case EaseFunctions.BackEaseInOut: return BackEaseInOut(t, from, to);
				case EaseFunctions.BackEaseOutIn: return BackEaseOutIn(t, from, to);
				default: return 0f;
			}
		}

		/// <summary>
		/// Interpolates between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector2 GetEase(EaseFunctions function, float t, Vector2 from, Vector2 to) {
			return VectorHelper.LerpWithOvershoot(from, to, GetEase(function, t));
		}

		/// <summary>
		/// Interpolates between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector3 GetEase(EaseFunctions function, float t, Vector3 from, Vector3 to) {
			return VectorHelper.LerpWithOvershoot(from, to, GetEase(function, t));
		}

		/// <summary>
		/// Interpolates between two quaternions with easing.
		/// </summary>
		/// <returns>The eased interpolated quaternion.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from quaternion; 1 = to quaternion</param>
		/// <param name="from">The from quaternion.</param>
		/// <param name="to">The to quaternion.</param>
		public static Quaternion GetEase(EaseFunctions function, float t, Quaternion from, Quaternion to) {
			return QuaternionHelper.SlerpWithOvershoot(from, to, GetEase(function, t));
		}
		
		/// <summary>
		/// Interpolates back and forth between two float values with easing.
		/// </summary>
		/// <returns>The eased interpolated float value.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float GetPingPong(EaseFunctions function, float t, float from = 0f, float to = 1) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(function, t - Mathf.Floor(t), from, to);
			else return GetEase(function, t - Mathf.Floor(t), to, from);
		}

		/// <summary>
		/// Interpolates back and forth between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector2 GetPingPong(EaseFunctions function, float t, Vector2 from, Vector2 to) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(function, t - Mathf.Floor(t), from, to);
			else return GetEase(function, t - Mathf.Floor(t), to, from);
		}

		/// <summary>
		/// Interpolates back and forth between two vectors with easing.
		/// </summary>
		/// <returns>The eased interpolated vector.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from vector; 1 = to vector</param>
		/// <param name="from">The from vector.</param>
		/// <param name="to">The to vector.</param>
		public static Vector3 GetPingPong(EaseFunctions function, float t, Vector3 from, Vector3 to) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(function, t - Mathf.Floor(t), from, to);
			else return GetEase(function, t - Mathf.Floor(t), to, from);
		}

		/// <summary>
		/// Interpolates back and forth between two quaternions with easing.
		/// </summary>
		/// <returns>The eased interpolated quaternion.</returns>
		/// <param name="function">The ease function used for easing.</param>
		/// <param name="t">The fraction. 0 = from quaternion; 1 = to quaternion</param>
		/// <param name="from">The from quaternion.</param>
		/// <param name="to">The to quaternion.</param>
		public static Quaternion GetPingPong(EaseFunctions function, float t, Quaternion from, Quaternion to) {
			if ((Mathf.Floor(t) % 2 > 0)) return GetEase(function, t - Mathf.Floor(t), from, to);
			else return GetEase(function, t - Mathf.Floor(t), to, from);
		}

		/// <summary>
		/// Linear interpolation.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float Linear(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			return from + to * t;
		}

		/// <summary>
		/// Hermite interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float Hermite(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);

			return Mathf.Lerp(from, to, t * t * (3.0f - 2.0f * t));
		}

		/// <summary>
		/// Boing interpolation that bounces once at the end.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float Boing(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);

			t = (Mathf.Sin(t * Mathf.PI * (0.2f + 2.5f * t * t * t)) * Mathf.Pow(1f - t, 2.2f) + t) * (1f + (1.2f * (1f - t)));
			return from + (to - from) * t;
		}

		/// <summary>
		/// Stationary bounce interpolation that bounces in the same position. Similar to the bouncing dock icons in osx.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float StationaryBounce(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);

			return (to - from) * Mathf.Abs(Mathf.Sin(6.28f * (t + 1f) * (t + 1f)) * (1f - t));
		}

		/// <summary>
		/// Exponential interpolation that eases out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ExponentialEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			if (t == 1f) return from + to;
			return from + to * (-Mathf.Pow(2f, -10f * t) + 1f);
		}

		/// <summary>
		/// Exponential interpolation that eases in.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ExponentialEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			if (t == 0f) return from;
			return from + to * Mathf.Pow(2f, 10f * (t - 1f));
		}

		/// <summary>
		/// Exponential interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ExponentialEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			if (t == 0f) return from;
			if (t == 1f) return from + to;
	
			if ((t /= 0.5f) < 1f) return from + to / 2f * Mathf.Pow(2f, 10f * (t - 1f));
			return from + to / 2f * (-Mathf.Pow(2f, -10f * --t) + 2f);
		}

		/// <summary>
		/// Exponential interpolation that eases in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ExponentialEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			if (t < 0.5f) return ExponentialEaseOut(t * 2f, from, to / 2f);
			return ExponentialEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Circular interpolation that eases out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CircularEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			return to * Mathf.Sqrt(1f - (t = t - 1f) * t) + from;
		}

		/// <summary>
		/// Circular interpolation that eases in.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CircularEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			return -to * (Mathf.Sqrt(1f - t * t) - 1f) + from;
		}

		/// <summary>
		/// Circular interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CircularEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			if ((t /= 0.5f) < 1f) return -to / 2f * (Mathf.Sqrt(1f - t * t) - 1f) + from;
			return to / 2f * (Mathf.Sqrt(1f - (t -= 2f) * t) + 1f) + from;
		}

		/// <summary>
		/// Circular interpolation that eases in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CircularEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);

			if (t < 0.5f) return CircularEaseOut(t * 2f, from, to / 2f);
			return CircularEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Quadratic interpolation that eases out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuadraticEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return -to * t * (t - 2f) + from;
		}

		/// <summary>
		/// Quadratic interpolation that eases in.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuadraticEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * t * t + from;
		}

		/// <summary>
		/// Quadratic interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuadraticEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if ((t /= 0.5f) < 1f) return to / 2f * t * t + from;
			return -to / 2f * ((--t) * (t - 2f) - 1f) + from;
		}

		/// <summary>
		/// Quadratic interpolation that eases in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuadraticEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return QuadraticEaseOut(t * 2f, from, to / 2f);
			return QuadraticEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Sine interpolation that eases out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float SineEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * Mathf.Sin(t * (Mathf.PI / 2f)) + from;
		}

		/// <summary>
		/// Sine interpolation that eases in.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float SineEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
	
			return -to * Mathf.Cos(t * (Mathf.PI / 2f)) + to + from;
		}

		/// <summary>
		/// Sine interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float SineEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);

			if ((t /= 0.5f) < 1f) return to / 2f * (Mathf.Sin(Mathf.PI * t / 2f)) + from;
			return -to / 2f * (Mathf.Cos(Mathf.PI * --t / 2f) - 2f) + from;
		}

		/// <summary>
		/// Sine interpolation that eases in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float SineEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return SineEaseOut(t * 2f, from, to / 2f);
			return SineEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Cubic interpolation that eases out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CubicEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * ((t = t - 1f) * t * t + 1f) + from;
		}

		/// <summary>
		/// Cubic interpolation that eases in.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CubicEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * t * t * t + from;
		}

		/// <summary>
		/// Cubic interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CubicEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if ((t /= 0.5f) < 1f) return to / 2f * t * t * t + from;
			return to / 2f * ((t -= 2f) * t * t + 2f) + from;
		}

		/// <summary>
		/// Cubic interpolation that eases in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float CubicEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return CubicEaseOut(t * 2f, from, to / 2f);
			return CubicEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Quartic interpolation that eases out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuarticEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return -to * ((t = t - 1f) * t * t * t - 1f) + from;
		}

		/// <summary>
		/// Quartic interpolation that eases in.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuarticEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * t * t * t * t + from;
		}
		/// <summary>
		/// Quartic interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		
		public static float QuarticEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if ((t /= 0.5f) < 1f) return to / 2f * t * t * t * t + from;
			return -to / 2f * ((t -= 2f) * t * t * t - 2f) + from;
		}

		/// <summary>
		/// Quartic interpolation that eases in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuarticEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return QuarticEaseOut(t * 2f, from, to / 2f);
			return QuarticEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Quintic interpolation that eases out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuinticEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * ((t = t - 1f) * t * t * t * t + 1f) + from;
		}

		/// <summary>
		/// Quintic interpolation that eases in.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuinticEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);

			return to * t * t * t * t * t + from;
		}

		/// <summary>
		/// Quintic interpolation that eases in and out.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuinticEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if ((t /= 0.5f) < 1f)
				return to / 2f * t * t * t * t * t + from;
			return to / 2f * ((t -= 2f) * t * t * t * t + 2f) + from;
		}

		/// <summary>
		/// Quintic interpolation that eases in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float QuinticEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return QuinticEaseOut(t * 2f, from, to / 2f);
			return QuinticEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}
		
		/// <summary>
		/// Elastic interpolation that eases out. It bounces and overshoots at the end.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ElasticEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t == 1f) return from + to;
			
			float p = .3f;
			float s = p / 4f;
			return (to * Mathf.Pow(2f, -10f * t) * Mathf.Sin((t - s) * (2f * Mathf.PI) / p) + to + from);
		}

		/// <summary>
		/// Elastic interpolation that eases in. It bounces and overshoots in the beginning.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ElasticEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t == 1f) return from + to;
			
			float p = .3f;
			float s = p / 4f;
			return -(to * Mathf.Pow(2f, 10f * (t -= 1f)) * Mathf.Sin((t - s) * (2f * Mathf.PI) / p)) + from;
		}

		/// <summary>
		/// Elastic interpolation that eases in and out. It bounces and overshoots in the beginning and at the end.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ElasticEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if ((t /= 0.5f) == 2f) return from + to;
			
			float p = .3f * 1.5f;
			float s = p / 4f;
			if (t < 1f) return -.5f * (to * Mathf.Pow(2f, 10f * (t -= 1f)) * Mathf.Sin((t - s) * (2f * Mathf.PI) / p)) + from;
			return to * Mathf.Pow(2f, -10f * (t -= 1f)) * Mathf.Sin((t - s) * (2f * Mathf.PI) / p) * .5f + to + from;
		}

		/// <summary>
		/// Elastic interpolation that eases in the middle. It bounces and overshoots in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float ElasticEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return ElasticEaseOut(t * 2f, from, to / 2f);
			return ElasticEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Bounce interpolation that eases out. It bounces but doesn't overshoot at the end.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BounceEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < (1f / 2.75f)) return to * (7.5625f * t * t) + from;
			else if (t < (2f / 2.75f)) return to * (7.5625f * (t -= (1.5f / 2.75f)) * t + .75f) + from;
			else if (t < (2.5f / 2.75f)) return to * (7.5625f * (t -= (2.25f / 2.75f)) * t + .9375f) + from;
			else return to * (7.5625f * (t -= (2.625f / 2.75f)) * t + .984375f) + from;
		}

		/// <summary>
		/// Bounce interpolation that eases in. It bounces but doesn't overshoot in the beginning.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BounceEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to - BounceEaseOut(1f - t, 0f, to) + from;
		}

		/// <summary>
		/// Bounce interpolation that eases in and out. It bounces but doesn't overshoot in the beginning and at the end.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BounceEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return BounceEaseIn(t * 2f, 0f, to) * .5f + from;
			else return BounceEaseOut(t * 2f - 1f, 0f, to) * .5f + to * .5f + from;
		}

		/// <summary>
		/// Bounce interpolation that eases in the middle. It bounces but doesn't overshoot in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BounceEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return BounceEaseOut(t * 2f, from, to / 2f);
			return BounceEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		/// <summary>
		/// Back interpolation that eases out. It overshoots once at the end.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BackEaseOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * ((t = t - 1f) * t * ((1.70158f + 1f) * t + 1.70158f) + 1f) + from;
		}

		/// <summary>
		/// Back interpolation that eases in. It overshoots once in the beginning.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BackEaseIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			return to * t * t * ((1.70158f + 1f) * t - 1.70158f) + from;
		}

		/// <summary>
		/// Back interpolation that eases in and out. It overshoots once in the beginning and once at the end.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BackEaseInOut(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			float s = 1.70158f;
			if ((t /= 0.5f) < 1f) return to / 2 * (t * t * (((s *= (1.525f)) + 1f) * t - s)) + from;
			return to / 2f * ((t -= 2f) * t * (((s *= (1.525f)) + 1f) * t + s) + 2f) + from;
		}

		/// <summary>
		/// Back interpolation that eases in the middle. It overshoots in both directions in the middle.
		/// </summary>
		/// <returns>The eased value.</returns>
		/// <param name="t">The fraction. 0 = from value; 1 = to value</param>
		/// <param name="from">The from value.</param>
		/// <param name="to">The to value.</param>
		public static float BackEaseOutIn(float t, float from = 0f, float to = 1f) {
			t = Mathf.Clamp(t, 0f, 1f);
			
			if (t < 0.5f) return BackEaseOut(t * 2f, from, to / 2f);
			return BackEaseIn((t * 2f) - 1f, from + to / 2f, to / 2f);
		}

		#endregion
	}
}
