using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/**
 * Interpolation utility functions: easing, bezier, and catmull-rom.
 * Consider using Unity's Animation curve editor and AnimationCurve class
 * before scripting the desired behaviour using this utility.
 *
 * Interpolation functionality available at different levels of abstraction.
 * Low level access via individual easing functions (ex. EaseInOutCirc),
 * Bezier(), and CatmullRom(). High level access using sequence generators,
 * NewEase(), NewBezier(), and NewCatmullRom().
 *
 * Sequence generators are typically used as follows:
 *
 * IEnumerable<Vector3> sequence = Interpolate.New[Ease|Bezier|CatmulRom](configuration);
 * foreach (Vector3 newPoint in sequence) {
 *   transform.position = newPoint;
 *   yield return WaitForSeconds(1.0f);
 * }
 *
 * Or:
 *
 * IEnumerator<Vector3> sequence = Interpolate.New[Ease|Bezier|CatmulRom](configuration).GetEnumerator();
 * function Update() {
 *   if (sequence.MoveNext()) {
 *     transform.position = sequence.Current;
 *   }
 * }
 *
 * The low level functions work similarly to Unity's built in Lerp and it is
 * up to you to track and pass in elapsedTime and duration on every call. The
 * functions take this form (or the logical equivalent for Bezier() and CatmullRom()).
 *
 * transform.position = ease(start, distance, elapsedTime, duration);
 *
 * For convenience in configuration you can use the Ease(EaseType) function to
 * look up a concrete easing function:
 * 
 *  [SerializeField]
 *  Interpolate.EaseType easeType; // set using Unity's property inspector
 *  Interpolate.Function ease; // easing of a particular EaseType
 * function Awake() {
 *   ease = Interpolate.Ease(easeType);
 * }
 *
 * @author Fernando Zapata (fernando@cpudreams.com)
 * @Traduzione Andrea85cs (andrea85cs@dynematica.it)
 */
 
public class Interpolate {
 
 
    /**
 * Different methods of easing interpolation.
 */
    public enum EaseType {
        Linear,
        EaseInQuad,
        EaseOutQuad,
        EaseInOutQuad,
        EaseInCubic,
        EaseOutCubic,
        EaseInOutCubic,
        EaseInQuart,
        EaseOutQuart,
        EaseInOutQuart,
        EaseInQuint,
        EaseOutQuint,
        EaseInOutQuint,
        EaseInSine,
        EaseOutSine,
        EaseInOutSine,
        EaseInExpo,
        EaseOutExpo,
        EaseInOutExpo,
        EaseInCirc,
        EaseOutCirc,
        EaseInOutCirc
    }
 
    /**
    * Sequence of eleapsedTimes until elapsedTime is >= duration.
    *
    * Note: elapsedTimes are calculated using the value of Time.deltatTime each
    * time a value is requested.
    */
    static Vector3 Identity(Vector3 v) {
        return v;
    }
 
    static Vector3 TransformDotPosition(Transform t) {
        return t.position;
    }
 
 
    static IEnumerable<float> NewTimer(float duration) {
        float elapsedTime = 0.0f;
        while (elapsedTime < duration) {
            yield return elapsedTime;
            elapsedTime += Time.deltaTime;
            // make sure last value is never skipped
            if (elapsedTime >= duration) {
                yield return elapsedTime;
            }
        }
    }
 
    public delegate Vector3 ToVector3<T>(T v);
    public delegate float Function(float a, float b, float c, float d);
 
    /**
     * Generates sequence of integers from start to end (inclusive) one step
     * at a time.
     */
    static IEnumerable<float> NewCounter(int start, int end, int step) {
        for (int i = start; i <= end; i += step) {
            yield return i;
        }
    }
 
    /**
     * Returns sequence generator from start to end over duration using the
     * given easing function. The sequence is generated as it is accessed
     * using the Time.deltaTime to calculate the portion of duration that has
     * elapsed.
     */
    public static IEnumerator NewEase(Function ease, Vector3 start, Vector3 end, float duration) {
        IEnumerable<float> timer = Interpolate.NewTimer(duration);
        return NewEase(ease, start, end, duration, timer);
    }
 
    /**
     * Instead of easing based on time, generate n interpolated points (slices)
     * between the start and end positions.
     */
    public static IEnumerator NewEase(Function ease, Vector3 start, Vector3 end, int slices) {
        IEnumerable<float> counter = Interpolate.NewCounter(0, slices + 1, 1);
        return NewEase(ease, start, end, slices + 1, counter);
    }
 
 
 
    /**
     * Generic easing sequence generator used to implement the time and
     * slice variants. Normally you would not use this function directly.
     */
    static IEnumerator NewEase(Function ease, Vector3 start, Vector3 end, float total, IEnumerable<float> driver) {
        Vector3 distance = end - start;
        foreach (float i in driver) {
            yield return Ease(ease, start, distance, i, total);
        }
    }
 
    /**
     * Vector3 interpolation using given easing method. Easing is done independently
     * on all three vector axis.
     */
    static Vector3 Ease(Function ease, Vector3 start, Vector3 distance, float elapsedTime, float duration) {
        start.x = ease(start.x, distance.x, elapsedTime, duration);
        start.y = ease(start.y, distance.y, elapsedTime, duration);
        start.z = ease(start.z, distance.z, elapsedTime, duration);
        return start;
    }
 
    /**
     * Returns the static method that implements the given easing type for scalars.
     * Use this method to easily switch between easing interpolation types.
     *
     * All easing methods clamp elapsedTime so that it is always <= duration.
     *
     * var ease = Interpolate.Ease(EaseType.EaseInQuad);
     * i = ease(start, distance, elapsedTime, duration);
     */
    public static Function Ease(EaseType type) {
        // Source Flash easing functions:
        // http://gizma.com/easing/
        // http://www.robertpenner.com/easing/easing_demo.html
        //
        // Changed to use more friendly variable names, that follow my Lerp
        // conventions:
        // start = b (start value)
        // distance = c (change in value)
        // elapsedTime = t (current time)
        // duration = d (time duration)
 
        Function f = null;
        switch (type) {
            case EaseType.Linear: f = Interpolate.Linear; break;
            case EaseType.EaseInQuad: f = Interpolate.EaseInQuad; break;
            case EaseType.EaseOutQuad: f = Interpolate.EaseOutQuad; break;
            case EaseType.EaseInOutQuad: f = Interpolate.EaseInOutQuad; break;
            case EaseType.EaseInCubic: f = Interpolate.EaseInCubic; break;
            case EaseType.EaseOutCubic: f = Interpolate.EaseOutCubic; break;
            case EaseType.EaseInOutCubic: f = Interpolate.EaseInOutCubic; break;
            case EaseType.EaseInQuart: f = Interpolate.EaseInQuart; break;
            case EaseType.EaseOutQuart: f = Interpolate.EaseOutQuart; break;
            case EaseType.EaseInOutQuart: f = Interpolate.EaseInOutQuart; break;
            case EaseType.EaseInQuint: f = Interpolate.EaseInQuint; break;
            case EaseType.EaseOutQuint: f = Interpolate.EaseOutQuint; break;
            case EaseType.EaseInOutQuint: f = Interpolate.EaseInOutQuint; break;
            case EaseType.EaseInSine: f = Interpolate.EaseInSine; break;
            case EaseType.EaseOutSine: f = Interpolate.EaseOutSine; break;
            case EaseType.EaseInOutSine: f = Interpolate.EaseInOutSine; break;
            case EaseType.EaseInExpo: f = Interpolate.EaseInExpo; break;
            case EaseType.EaseOutExpo: f = Interpolate.EaseOutExpo; break;
            case EaseType.EaseInOutExpo: f = Interpolate.EaseInOutExpo; break;
            case EaseType.EaseInCirc: f = Interpolate.EaseInCirc; break;
            case EaseType.EaseOutCirc: f = Interpolate.EaseOutCirc; break;
            case EaseType.EaseInOutCirc: f = Interpolate.EaseInOutCirc; break;
        }
        return f;
    }
 
    /**
     * Returns sequence generator from the first node to the last node over
     * duration time using the points in-between the first and last node
     * as control points of a bezier curve used to generate the interpolated points
     * in the sequence. If there are no control points (ie. only two nodes, first
     * and last) then this behaves exactly the same as NewEase(). In other words
     * a zero-degree bezier spline curve is just the easing method. The sequence
     * is generated as it is accessed using the Time.deltaTime to calculate the
     * portion of duration that has elapsed.
     */
    public static IEnumerable<Vector3> NewBezier(Function ease, Transform[] nodes, float duration) {
        IEnumerable<float> timer = Interpolate.NewTimer(duration);
        return NewBezier<Transform>(ease, nodes, TransformDotPosition, duration, timer);
    }
 
    /**
     * Instead of interpolating based on time, generate n interpolated points
     * (slices) between the first and last node.
     */
    public static IEnumerable<Vector3> NewBezier(Function ease, Transform[] nodes, int slices) {
        IEnumerable<float> counter = NewCounter(0, slices + 1, 1);
        return NewBezier<Transform>(ease, nodes, TransformDotPosition, slices + 1, counter);
    }
 
    /**
     * A Vector3[] variation of the Transform[] NewBezier() function.
     * Same functionality but using Vector3s to define bezier curve.
     */
    public static IEnumerable<Vector3> NewBezier(Function ease, Vector3[] points, float duration) {
        IEnumerable<float> timer = NewTimer(duration);
        return NewBezier<Vector3>(ease, points, Identity, duration, timer);
    }
 
    /**
     * A Vector3[] variation of the Transform[] NewBezier() function.
     * Same functionality but using Vector3s to define bezier curve.
     */
    public static IEnumerable<Vector3> NewBezier(Function ease, Vector3[] points, int slices) {
        IEnumerable<float> counter = NewCounter(0, slices + 1, 1);
        return NewBezier<Vector3>(ease, points, Identity, slices + 1, counter);
    }
 
    /**
     * Generic bezier spline sequence generator used to implement the time and
     * slice variants. Normally you would not use this function directly.
     */
    static IEnumerable<Vector3> NewBezier<T>(Function ease, IList nodes, ToVector3<T> toVector3, float maxStep, IEnumerable<float> steps) {
        // need at least two nodes to spline between
        if (nodes.Count >= 2) {
            // copy nodes array since Bezier is destructive
            Vector3[] points = new Vector3[nodes.Count];
 
            foreach (float step in steps) {
                // re-initialize copy before each destructive call to Bezier
                for (int i = 0; i < nodes.Count; i++) {
                    points[i] = toVector3((T)nodes[i]);
                }
                yield return Bezier(ease, points, step, maxStep);
                // make sure last value is always generated
            }
        }
    }
 
    /**
     * A Vector3 n-degree bezier spline.
     *
     * WARNING: The points array is modified by Bezier. See NewBezier() for a
     * safe and user friendly alternative.
     *
     * You can pass zero control points, just the start and end points, for just
     * plain easing. In other words a zero-degree bezier spline curve is just the
     * easing method.
     *
     * @param points start point, n control points, end point
     */
    static Vector3 Bezier(Function ease, Vector3[] points, float elapsedTime, float duration) {
        // Reference: http://ibiblio.org/e-notes/Splines/Bezier.htm
        // Interpolate the n starting points to generate the next j = (n - 1) points,
        // then interpolate those n - 1 points to generate the next n - 2 points,
        // continue this until we have generated the last point (n - (n - 1)), j = 1.
        // We store the next set of output points in the same array as the
        // input points used to generate them. This works because we store the
        // result in the slot of the input point that is no longer used for this
        // iteration.
        for (int j = points.Length - 1; j > 0; j--) {
            for (int i = 0; i < j; i++) {
                points[i].x = ease(points[i].x, points[i + 1].x - points[i].x, elapsedTime, duration);
                points[i].y = ease(points[i].y, points[i + 1].y - points[i].y, elapsedTime, duration);
                points[i].z = ease(points[i].z, points[i + 1].z - points[i].z, elapsedTime, duration);
            }
        }
        return points[0];
    }
 
    /**
     * Returns sequence generator from the first node, through each control point,
     * and to the last node. N points are generated between each node (slices)
     * using Catmull-Rom.
     */
    public static IEnumerable<Vector3> NewCatmullRom(Transform[] nodes, int slices, bool loop) {
        return NewCatmullRom<Transform>(nodes, TransformDotPosition, slices, loop);
    }
 
    /**
     * A Vector3[] variation of the Transform[] NewCatmullRom() function.
     * Same functionality but using Vector3s to define curve.
     */
    public static IEnumerable<Vector3> NewCatmullRom(Vector3[] points, int slices, bool loop) {
        return NewCatmullRom<Vector3>(points, Identity, slices, loop);
    }
 
    /**
     * Generic catmull-rom spline sequence generator used to implement the
     * Vector3[] and Transform[] variants. Normally you would not use this
     * function directly.
     */
    static IEnumerable<Vector3> NewCatmullRom<T>(IList nodes, ToVector3<T> toVector3, int slices, bool loop) {
        // need at least two nodes to spline between
        if (nodes.Count >= 2) {
 
            // yield the first point explicitly, if looping the first point
            // will be generated again in the step for loop when interpolating
            // from last point back to the first point
            yield return toVector3((T)nodes[0]);
 
            int last = nodes.Count - 1;
            for (int current = 0; loop || current < last; current++) {
                // wrap around when looping
                if (loop && current > last) {
                    current = 0;
                }
                // handle edge cases for looping and non-looping scenarios
                // when looping we wrap around, when not looping use start for previous
                // and end for next when you at the ends of the nodes array
                int previous = (current == 0) ? ((loop) ? last : current) : current - 1;
                int start = current;
                int end = (current == last) ? ((loop) ? 0 : current) : current + 1;
                int next = (end == last) ? ((loop) ? 0 : end) : end + 1;
 
                // adding one guarantees yielding at least the end point
                int stepCount = slices + 1;
                for (int step = 1; step <= stepCount; step++) {
                    yield return CatmullRom(toVector3((T)nodes[previous]),
                                     toVector3((T)nodes[start]),
                                     toVector3((T)nodes[end]),
                                     toVector3((T)nodes[next]),
                                     step, stepCount);
                }
            }
        }
    }
 
    /**
     * A Vector3 Catmull-Rom spline. Catmull-Rom splines are similar to bezier
     * splines but have the useful property that the generated curve will go
     * through each of the control points.
     *
     * NOTE: The NewCatmullRom() functions are an easier to use alternative to this
     * raw Catmull-Rom implementation.
     *
     * @param previous the point just before the start point or the start point
     *                 itself if no previous point is available
     * @param start generated when elapsedTime == 0
     * @param end generated when elapsedTime >= duration
     * @param next the point just after the end point or the end point itself if no
     *             next point is available
     */
    static Vector3 CatmullRom(Vector3 previous, Vector3 start, Vector3 end, Vector3 next, 
                                float elapsedTime, float duration) {
        // References used:
        // p.266 GemsV1
        //
        // tension is often set to 0.5 but you can use any reasonable value:
        // http://www.cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
        //
        // bias and tension controls:
        // http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/interpolation/
 
        float percentComplete = elapsedTime / duration;
        float percentCompleteSquared = percentComplete * percentComplete;
        float percentCompleteCubed = percentCompleteSquared * percentComplete;
 
        return previous * (-0.5f * percentCompleteCubed +
                                   percentCompleteSquared -
                            0.5f * percentComplete) +
                start   * ( 1.5f * percentCompleteCubed +
                           -2.5f * percentCompleteSquared + 1.0f) +
                end     * (-1.5f * percentCompleteCubed +
                            2.0f * percentCompleteSquared +
                            0.5f * percentComplete) +
                next    * ( 0.5f * percentCompleteCubed -
                            0.5f * percentCompleteSquared);
    }
 
 
 
 
    /**
     * Linear interpolation (same as Mathf.Lerp)
     */
    static float Linear(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime to be <= duration
        if (elapsedTime > duration) { elapsedTime = duration; }
        return distance * (elapsedTime / duration) + start;
    }
 
    /**
     * quadratic easing in - accelerating from zero velocity
     */
    static float EaseInQuad(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        return distance * elapsedTime * elapsedTime + start;
    }
 
    /**
     * quadratic easing out - decelerating to zero velocity
     */
    static float EaseOutQuad(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        return -distance * elapsedTime * (elapsedTime - 2) + start;
    }
 
    /**
     * quadratic easing in/out - acceleration until halfway, then deceleration
     */
    static float EaseInOutQuad(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 2.0f : elapsedTime / (duration / 2);
        if (elapsedTime < 1) return distance / 2 * elapsedTime * elapsedTime + start;
        elapsedTime--;
        return -distance / 2 * (elapsedTime * (elapsedTime - 2) - 1) + start;
    }
 
    /**
     * cubic easing in - accelerating from zero velocity
     */
    static float EaseInCubic(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        return distance * elapsedTime * elapsedTime * elapsedTime + start;
    }
 
    /**
     * cubic easing out - decelerating to zero velocity
     */
    static float EaseOutCubic(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        elapsedTime--;
        return distance * (elapsedTime * elapsedTime * elapsedTime + 1) + start;
    }
 
    /**
     * cubic easing in/out - acceleration until halfway, then deceleration
     */
    static float EaseInOutCubic(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 2.0f : elapsedTime / (duration / 2);
        if (elapsedTime < 1) return distance / 2 * elapsedTime * elapsedTime * elapsedTime + start;
        elapsedTime -= 2;
        return distance / 2 * (elapsedTime * elapsedTime * elapsedTime + 2) + start;
    }
 
    /**
     * quartic easing in - accelerating from zero velocity
     */
    static float EaseInQuart(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        return distance * elapsedTime * elapsedTime * elapsedTime * elapsedTime + start;
    }
 
    /**
     * quartic easing out - decelerating to zero velocity
     */
    static float EaseOutQuart(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        elapsedTime--;
        return -distance * (elapsedTime * elapsedTime * elapsedTime * elapsedTime - 1) + start;
    }
 
    /**
     * quartic easing in/out - acceleration until halfway, then deceleration
     */
    static float EaseInOutQuart(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 2.0f : elapsedTime / (duration / 2);
        if (elapsedTime < 1) return distance / 2 * elapsedTime * elapsedTime * elapsedTime * elapsedTime + start;
        elapsedTime -= 2;
        return -distance / 2 * (elapsedTime * elapsedTime * elapsedTime * elapsedTime - 2) + start;
    }
 
 
    /**
     * quintic easing in - accelerating from zero velocity
     */
    static float EaseInQuint(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        return distance * elapsedTime * elapsedTime * elapsedTime * elapsedTime * elapsedTime + start;
    }
 
    /**
     * quintic easing out - decelerating to zero velocity
     */
    static float EaseOutQuint(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        elapsedTime--;
        return distance * (elapsedTime * elapsedTime * elapsedTime * elapsedTime * elapsedTime + 1) + start;
    }
 
    /**
     * quintic easing in/out - acceleration until halfway, then deceleration
     */
    static float EaseInOutQuint(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 2.0f : elapsedTime / (duration / 2f);
        if (elapsedTime < 1) return distance / 2 * elapsedTime * elapsedTime * elapsedTime * elapsedTime * elapsedTime + start;
        elapsedTime -= 2;
        return distance / 2 * (elapsedTime * elapsedTime * elapsedTime * elapsedTime * elapsedTime + 2) + start;
    }
 
    /**
     * sinusoidal easing in - accelerating from zero velocity
     */
    static float EaseInSine(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime to be <= duration
        if (elapsedTime > duration) { elapsedTime = duration; }
        return -distance * Mathf.Cos(elapsedTime / duration * (Mathf.PI / 2)) + distance + start;
    }
 
    /**
     * sinusoidal easing out - decelerating to zero velocity
     */
    static float EaseOutSine(float start, float distance, float elapsedTime, float duration) {
        if (elapsedTime > duration) { elapsedTime = duration; }
        return distance * Mathf.Sin(elapsedTime / duration * (Mathf.PI / 2)) + start;
    }
 
    /**
     * sinusoidal easing in/out - accelerating until halfway, then decelerating
     */
    static float EaseInOutSine(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime to be <= duration
        if (elapsedTime > duration) { elapsedTime = duration; }
        return -distance / 2 * (Mathf.Cos(Mathf.PI * elapsedTime / duration) - 1) + start;
    }
 
    /**
     * exponential easing in - accelerating from zero velocity
     */
    static float EaseInExpo(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime to be <= duration
        if (elapsedTime > duration) { elapsedTime = duration; }
        return distance * Mathf.Pow(2, 10 * (elapsedTime / duration - 1)) + start;
    }
 
    /**
     * exponential easing out - decelerating to zero velocity
     */
    static float EaseOutExpo(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime to be <= duration
        if (elapsedTime > duration) { elapsedTime = duration; }
        return distance * (-Mathf.Pow(2, -10 * elapsedTime / duration) + 1) + start;
    }
 
    /**
     * exponential easing in/out - accelerating until halfway, then decelerating
     */
    static float EaseInOutExpo(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 2.0f : elapsedTime / (duration / 2);
        if (elapsedTime < 1) return distance / 2 *  Mathf.Pow(2, 10 * (elapsedTime - 1)) + start;
        elapsedTime--;
        return distance / 2 * (-Mathf.Pow(2, -10 * elapsedTime) + 2) + start;
    }
 
    /**
     * circular easing in - accelerating from zero velocity
     */
    static float EaseInCirc(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        return -distance * (Mathf.Sqrt(1 - elapsedTime * elapsedTime) - 1) + start;
    }
 
    /**
     * circular easing out - decelerating to zero velocity
     */
    static float EaseOutCirc(float start, float distance, float elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 1.0f : elapsedTime / duration;
        elapsedTime--;
        return distance * Mathf.Sqrt(1 - elapsedTime * elapsedTime) + start;
    }
 
    /**
     * circular easing in/out - acceleration until halfway, then deceleration
     */
    static float EaseInOutCirc(float start, float distance, float
                         elapsedTime, float duration) {
        // clamp elapsedTime so that it cannot be greater than duration
        elapsedTime = (elapsedTime > duration) ? 2.0f : elapsedTime / (duration / 2);
        if (elapsedTime < 1) return -distance / 2 * (Mathf.Sqrt(1 - elapsedTime * elapsedTime) - 1) + start;
        elapsedTime -= 2;
        return distance / 2 * (Mathf.Sqrt(1 - elapsedTime * elapsedTime) + 1) + start;
    }
}