/*
<body bgcolor=white>
<h2>
Java class to implement cubic and bicubic splines
</h2>
&nbsp; &nbsp; &nbsp;
by Ken Perlin @ NYU, 1998, 2004.
<p>
<font color=red><i>
You have my permission to use freely, as long
as you keep the attribution. - Ken Perlin
</i></font>
<p>
<h3>
What does the class do?
</h3>
<i>
<ol>
<li>
<b>Cubic spline:</b>
If you provide various geometric values for t, then
this class creates an object that will
interpolate a Cubic spline
to give you the value
within any value of t between 0 and 1.
<p>
If you want
to create a spline path,
you can make a one dimensional
array of such objects.
<p>
<li>
<b>Bicubic spline:</b>
If you provide a 4x4 grid of values
for geometric quantities in u and v,
this class creates an object that will
interpolate a Bicubic spline
to give you the value
within any point of a unit tile in (u,v) space.
<p>
If you want
to create a spline surface,
you can make a two dimensional
array of such objects.
</ol>
</i>




<hr>
<h3> For a cubic spline the class provides a constructor and a method: </h3>

<p>
<blockquote>
<tt>
	Cubic(double[] G)
</tt>
<blockquote>
		Given four geometric values over t,
		calculate cubic coefficients.
</blockquote>
<tt>
	double eval(double t)
</tt>
<blockquote>
		Given a point in the interval t = [0 ... 1],
		return a value.
</blockquote>

<i>Algorithm:</i>

<blockquote>
f(t) = T M G<sup>T</sup>, where:
<blockquote>
T = (t<sup>3</sup> t<sup>2</sup> t 1) ,
<br>
M is the basis matrix.
</blockquote>
The constructor <tt>Cubic(G)</tt> calculates the matrix C = M G<sup>T</sup>
<p>
The method <tt>eval(t)</tt> calculates the value T C
</blockquote>

</blockquote>

</blockquote>

<hr>
<h3> For a bicubic spline the class provides a constructor and a method: </h3>

<p>
<blockquote>
<tt>
	Cubic(double[][] G)
</tt>
<blockquote>
		Given 4&#215;4 geometric values over u&#215;v,
		calculate bicubic coefficients.
</blockquote>
<tt>
	double eval(double u, double v)
</tt>
<blockquote>
		Given a point in the square [0 ... 1] &#215; [0 ... 1],
		return a value.
</blockquote>


<i>Algorithm:</i>
<blockquote>

f(u,v) = U M G M<sup>T</sup> V<sup>T</sup>  , where:

<blockquote>
U = (u<sup>3</sup> u<sup>2</sup> u 1) ,
<br>
V = (v<sup>3</sup> v<sup>2</sup> v 1) ,
<br>
M is the basis matrix.
</blockquote>
The constructor <tt>Cubic(G)</tt> calculates the matrix C = M G M<sup>T</sup>
<p>
The method <tt>eval(u,v)</tt> calculates the value U C V<sup>T</sup>
</blockquote>

</blockquote>

<hr><pre>
*/

public class Cubic
{
   public static final double[][] BEZIER = {      //<b> Bezier basis matrix</b>
      {-1  ,  3  , -3  , 1  },
      { 3  , -6  ,  3  , 0  },
      {-3  ,  3  ,  0  , 0  },
      { 1  ,  0  ,  0  , 0  } 
   };
   public static final double[][] BSPLINE = {     //<b> BSpline basis matrix</b>
      {-1./6 ,  3./6 , -3./6 , 1./6 },
      { 3./6 , -6./6 ,  3./6 , 0.   },
      {-3./6 ,  0.   ,  3./6 , 0.   },
      { 1./6 ,  4./6 ,  1./6 , 0.   } 
   };
   public static final double[][] CATMULL_ROM = { //<b> Catmull-Rom basis matrix</b>
      {-0.5 ,  1.5 , -1.5 ,  0.5 },
      { 1   , -2.5 ,  2   , -0.5 },
      {-0.5 ,  0   ,  0.5 ,  0   },
      { 0   ,  1   ,  0   ,  0   } 
   };
   public static final double[][] HERMITE = {     //<b> Hermite basis matrix</b>
      { 2  , -2  ,  1  ,  1  },
      {-3  ,  3  , -2  , -1  },
      { 0  ,  0  ,  1  ,  0  },
      { 1  ,  0  ,  0  ,  0  } 
   };

   double a, b, c, d;                  //<b> cubic coefficients vector</b>

   Cubic(double[][] M, double[] G) {
      a = b = c = d;
      for (int k = 0 ; k < 4 ; k++) {  //<b> (a,b,c,d) = M G</b>
	 a += M[0][k] * G[k];
	 b += M[1][k] * G[k];
	 c += M[2][k] * G[k];
	 d += M[3][k] * G[k];
      }
   }

   public double eval(double t) {
      return t * (t * (t * a + b) + c) + d;
   }

   double[][] C = new double[4][4];    //<b> bicubic coefficients matrix</b>
   double[][] T = new double[4][4];    //<b> scratch matrix</b>

   Cubic(double[][] M, double[][] G) {
      for (int i = 0 ; i < 4 ; i++)    //<b> T = G M<sup>T</sup></b>
      for (int j = 0 ; j < 4 ; j++)
      for (int k = 0 ; k < 4 ; k++)
	 T[i][j] += G[i][k] * M[j][k];
      
      for (int i = 0 ; i < 4 ; i++)    //<b> C = M T</b>
      for (int j = 0 ; j < 4 ; j++)
      for (int k = 0 ; k < 4 ; k++)
	 C[i][j] += M[i][k] * T[k][j];
   }

   double[] C3 = C[0], C2 = C[1], C1 = C[2], C0 = C[3];

   public double eval(double u, double v) {
      return u * (u * (u * (v * (v * (v * C3[0] + C3[1]) + C3[2]) + C3[3])
                         + (v * (v * (v * C2[0] + C2[1]) + C2[2]) + C2[3]))
                         + (v * (v * (v * C1[0] + C1[1]) + C1[2]) + C1[3]))
                         + (v * (v * (v * C0[0] + C0[1]) + C0[2]) + C0[3]);
   }
}

