コード例 #1
0
ファイル: SplineBase.cs プロジェクト: Maestroxr/Math
        /// <summary>
        /// Converts length parameter [0..TotalLength] to time parameter [0..1]
        /// </summary>
        /// <param name="length">Distance parameter to convert into time parameter</param>
        /// <param name="iterations">Number of iterations used in internal calculations, default is 32</param>
        /// <param name="tolerance">Small positive number, e.g. 1e-5f</param>
        public float LengthToTime(float length, int iterations, float tolerance)
        {
            // Update length information if necessary
            if (_data.Count < 2)
            {
                return(0f);
            }
            RecalcSegmentsLength();

            int segmentCount = SegmentCount;
            int lastSegment  = segmentCount - 1;

            // Check out of bounds length
            if (length <= 0f)
            {
                return(0f);
            }
            if (length >= _data[lastSegment].AccumulatedLength)
            {
                return(1f);
            }

            //TODO: use binary search
            int segmentIndex;

            for (segmentIndex = 0; segmentIndex < segmentCount; ++segmentIndex)
            {
                if (length < _data[segmentIndex].AccumulatedLength)
                {
                    break;
                }
            }

            ItemData segment          = _data[segmentIndex];
            float    segmentStartTime = (float)segmentIndex;          // Uniform parametrization with unit length time per segment

            float len0 = segmentIndex == 0 ? length : (length - _data[segmentIndex - 1].AccumulatedLength);
            float len1 = _data[segmentIndex].Length;

            // If L(t) is the length function for t in [tmin,tmax], the derivative is
            // L'(t) = |x'(t)| >= 0 (the magnitude of speed).  Therefore, L(t) is a
            // nondecreasing function (and it is assumed that x'(t) is zero only at
            // isolated points; that is, no degenerate curves allowed).  The second
            // derivative is L"(t).  If L"(t) >= 0 for all t, L(t) is a convex
            // function and Newton's method for root finding is guaranteed to
            // converge.  However, L"(t) can be negative, which can lead to Newton
            // iterates outside the domain [tmin,tmax].  The algorithm here avoids
            // this problem by using a hybrid of Newton's method and bisection.

            // Initial guess for Newton's method is dt0.
            float dt1 = 1f;
            float dt0 = dt1 * len0 / len1;

            // Initial root-bounding interval for bisection.
            float lower = 0f;
            float upper = dt1;

            for (int i = 0; i < iterations; ++i)
            {
                float difference = segment.EvalLength(0, dt0) - len0;
                if (UnityEngine.Mathf.Abs(difference) <= tolerance)
                {
                    // |L(mTimes[key]+dt0)-length| is close enough to zero, report
                    // mTimes[key]+dt0 as the time at which 'length' is attained.
                    return((segmentStartTime + dt0) / segmentCount);
                }

                // Generate a candidate for Newton's method.
                float dt0Candidate = dt0 - difference / segment.EvalSpeed(dt0);

                // Update the root-bounding interval and test for containment of the candidate.
                if (difference > 0f)
                {
                    upper = dt0;
                    if (dt0Candidate <= lower)
                    {
                        // Candidate is outside the root-bounding interval. Use bisection instead.
                        dt0 = 0.5f * (upper + lower);
                    }
                    else
                    {
                        // There is no need to compare to 'upper' because the tangent line has
                        // positive slope, guaranteeing that the t-axis intercept is smaller than 'upper'.
                        dt0 = dt0Candidate;
                    }
                }
                else
                {
                    lower = dt0;
                    if (dt0Candidate >= upper)
                    {
                        // Candidate is outside the root-bounding interval. Use bisection instead.
                        dt0 = 0.5f * (upper + lower);
                    }
                    else
                    {
                        // There is no need to compare to 'lower' because the tangent line has
                        // positive slope, guaranteeing that the t-axis intercept is larger than 'lower'.
                        dt0 = dt0Candidate;
                    }
                }
            }

            // A root was not found according to the specified number of iterations
            // and tolerance.  You might want to increase iterations or tolerance or
            // integration accuracy.  However, in this application it is likely that
            // the time values are oscillating, due to the limited numerical
            // precision of 32-bit floats.  It is safe to use the last computed time.
            return((segmentStartTime + dt0) / segmentCount);
        }