Ejemplo n.º 1
0
        /// <summary>
        /// Fits a cubic spline to the list of control points.
        /// </summary>
        /// <param name="transferPoints">Control points to fit a cubic spline to.</param>
        public void Calculate(List <T> controlPoints)
        {
            Debug.Assert(controlPoints.Count >= 3, "A cubic spline cannot be calculated with less " +
                         "than three points. Otherwise it is just a straight line (with two points).");

            // We need to solve the equation taken from: http://mathworld.wolfram.com/CubicSpline.html,
            //
            // [2 1      0] [D[0]]   [3(y[1] - y[0])  ]
            // |1 4 1     | |D[1]|   |3(y[2] - y[0])  |
            // |  1 4 1   | | .  | = |       .        |
            // |    ..... | | .  |   |       .        |
            // |     1 4 1| | .  |   |3(y[n] - y[n-2])|
            // [0      1 2] [D[n]]   [3(y[n] - y[n-1])]
            //
            // where n is the number of cubics and D[i] are the derivatives at the control points.
            // This linear system of equations happens to be tridiagonal and therefore can be solved
            // using the Thomas algorithm (aka TDMA), which is a simplified form of Gaussian elimination
            // that can obtain the solution in O(n) instead of O(n^3) required by Gaussian elimination.
            // The Thomas algorithm was taken from: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm.

            int numCubics = controlPoints.Count - 1;
            T   t         = new T();

            // Construct the coefficients a,b,c and the right-hand-side vector d.
            T[,] matrixCoef = new T[numCubics + 1, 4];

            matrixCoef[0, 0] = t.Zero;
            matrixCoef[0, 1] = t.Two;
            matrixCoef[0, 2] = t.One;
            matrixCoef[0, 3] = t.Mult(t.Sub(controlPoints[1], controlPoints[0]), 3f);

            for (int i = 1; i < numCubics; ++i)
            {
                matrixCoef[i, 0] = t.One;
                matrixCoef[i, 1] = t.Four;
                matrixCoef[i, 2] = t.One;
                matrixCoef[i, 3] = t.Mult(t.Sub(controlPoints[i + 1], controlPoints[i - 1]), 3f);
            }

            matrixCoef[numCubics, 0] = t.One;
            matrixCoef[numCubics, 1] = t.Two;
            matrixCoef[numCubics, 2] = t.Zero;
            matrixCoef[numCubics, 3] = t.Mult(t.Sub(controlPoints[numCubics], controlPoints[numCubics - 1]), 3f);

            // Modify the coefficients (Thomas algorithm).
            matrixCoef[0, 2] = t.Div(matrixCoef[0, 2], matrixCoef[0, 1]);
            matrixCoef[0, 3] = t.Div(matrixCoef[0, 3], matrixCoef[0, 1]);

            for (int i = 1; i < numCubics; ++i)
            {
                T val = t.Sub(matrixCoef[i, 1], t.Mult(matrixCoef[i - 1, 2], matrixCoef[i, 0]));
                matrixCoef[i, 2] = t.Div(matrixCoef[i, 2], val);
                matrixCoef[i, 3] = t.Div(t.Sub(matrixCoef[i, 3], t.Mult(matrixCoef[i - 1, 3], matrixCoef[i, 0])), val);
            }

            // Back substitute to solve for the derivaties (Thomas algorithm).
            T[] derivatives = new T[numCubics + 1];

            derivatives[numCubics - 1] = matrixCoef[numCubics - 1, 3];
            for (int i = numCubics - 2; i >= 0; --i)
            {
                derivatives[i] = t.Sub(matrixCoef[i, 3], t.Mult(matrixCoef[i, 2], derivatives[i + 1]));
            }

            // Use the derivatives to solve for the coefficients of the cubics.
            cubics = new Cubic <T> [numCubics];
            for (int i = 0; i < numCubics; ++i)
            {
                T a = controlPoints[i];
                T b = derivatives[i];
                T c = t.Sub(t.Sub(t.Mult(t.Sub(controlPoints[i + 1], controlPoints[i]), 3f), t.Mult(derivatives[i], 2f)), derivatives[i + 1]);
                T d = t.Add(t.Add(t.Mult(t.Sub(controlPoints[i], controlPoints[i + 1]), 2f), derivatives[i]), derivatives[i + 1]);
                cubics[i] = new Cubic <T>(a, b, c, d);
            }
        }