using System;
using Microsoft.Xna.Framework;

namespace MikuMikuDance.XNA.Motion.MotionData
{
    /// <summary>
    /// ベジェ曲線
    /// </summary>
    /// <remarks>
    /// MMDで使われていると(スレで)言われているベジェ曲線計算クラスです。
    /// 原理はベジェ曲線作って、ニュートン法で近似解計算しているだけです。
    /// ベジェ曲線は(http://musashi.or.tv/fontguide_doc3.htm)を参考にさせてもらいました
    /// </remarks>
    public struct BezierCurve
    {
        internal const float Epsilon = 1.0e-3f;
        /// <summary>
        /// ベジェ曲線に用いる点１
        /// </summary>
        public Vector2 v1;
        /// <summary>
        /// ベジェ曲線に用いる点2
        /// </summary>
        public Vector2 v2;
        /// <summary>
        /// 進行度合から移行度合を取得
        /// </summary>
        /// <param name="Progress">進行度合</param>
        /// <returns>移行度合</returns>
        public float Evaluate(float Progress)
        {
            //ニュートン法による近似
            float t = MathHelper.Clamp(Progress, 0, 1);
            float dt;
            do
            {
                dt = -(fx(t) - Progress) / dfx(t);
                if (float.IsNaN(dt))
                    break;
                t += MathHelper.Clamp(dt, -1f, 1f);//大幅に移動して別の解に到達するのを防止する用
            } while (Math.Abs(dt) > Epsilon);
            return MathHelper.Clamp(fy(t), 0f, 1f);//念のため、0-1の間に収まるようにした
        }

        private float fy(float t)
        {
            //fy(t)=(1-t)^3*0+3*(1-t)^2*t*v1.y+3*(1-t)*t^2*v2.y+t^3*1
            return 3 * (1 - t) * (1 - t) * t * v1.Y + 3 * (1 - t) * t * t * v2.Y + t * t * t;
        }

        float fx(float t)
        {
            //fx(t)=(1-t)^3*0+3*(1-t)^2*t*v1.x+3*(1-t)*t^2*v2.x+t^3*1
            return 3 * (1 - t) * (1 - t) * t * v1.X + 3 * (1 - t) * t * t * v2.X + t * t * t;
        }
        float dfx(float t)
        {
            //dfx(t)/dt=-6(1-t)*t*v1.x+3(1-t)^2*v1.x-3t^2*v2.x+6(1-t)*t*v2.x+3t^2
            return -6 * (1 - t) * t * v1.X + 3 * (1 - t) * (1 - t) * v1.X
                - 3 * t * t * v2.X + 6 * (1 - t) * t * v2.X + 3 * t * t;
        }

    }
}