/* :ts=8 */
/************************************************************************/
/*                                                                      */
/* This file contains the hairy details of rotating and computing points*/
/* on splines.								*/
/* 									*/
/* This file is a good candidate for optimization!			*/
/*                                                                      */
/************************************************************************/
#include  <math.h>
#include  <stdio.h>
#include <string.h>
#include <ctype.h>

#include "general.h"
#include "globals.h"
#include "spl_math.h"

	/* The hermite basis matrix (Computer Graphics p. 484) */
static Matrix4_T	M_Hermite	= {
	 2,	-2,	 1,	 1,
	-3,	 3,	-2,	-1,
	 0,	 0,	 1,	 0,
	 1,	 0,	 0,	 0
};

void Matrix_Mult(Matrix_T MRes, Matrix_T M1, Matrix_T M2)
/************************************************************************/
/*                                                                      */
/*                          ====   ==   ==                              */
/* 			    MRes = M1 * M2				*/
/*                                                                      */
/************************************************************************/
{
    int		i, j;

    for (i = 0; i < 3; i++) 
	for (j = 0; j < 3; j++) 
	    MRes[i][j] = M1[i][0] * M2[0][j] + 
	                 M1[i][1] * M2[1][j] + 
	                 M1[i][2] * M2[2][j];

} /* Matrix_Mult */


void Matrix4_Mult(Matrix4_T MRes, Matrix4_T M1, Matrix4_T M2)
/************************************************************************/
/*                                                                      */
/*                          ====   ==   ==                              */
/* 			    MRes = M1 * M2				*/
/*                                                                      */
/************************************************************************/
{
    int		i, j;

    for (i = 0; i < 4; i++) 
	for (j = 0; j < 4; j++) 
	    MRes[i][j] = M1[i][0] * M2[0][j] + 
	                 M1[i][1] * M2[1][j] + 
	                 M1[i][2] * M2[2][j] + 
	                 M1[i][3] * M2[3][j]; 

} /* Matrix4_Mult */

void Matrix_VMult(Vector_T VRes, Vector_T V, Matrix_T M)
/************************************************************************/
/*                                                                      */
/*                          ----   -   =                                */
/* 			    VRes = V * M				*/
/*                                                                      */
/************************************************************************/
{
    int		i;

    for (i = 0; i < 3; i++) 
    	VRes[i] = V[0] * M[0][i] +
    	          V[1] * M[1][i] +
    	          V[2] * M[2][i];

} /* Matrix_VMult */


void Matrix4_VMult(Vector4_T VRes, Vector4_T V, Matrix4_T M)
/************************************************************************/
/*                                                                      */
/*                          ----   -   =                                */
/* 			    VRes = V * M				*/
/*                                                                      */
/************************************************************************/
{
    int		i;

    for (i = 0; i < 4; i++) 
    	VRes[i] = V[0] * M[0][i] +
    	          V[1] * M[1][i] +
    	          V[2] * M[2][i] +
    	          V[3] * M[3][i];

} /* Matrix4_VMult */

void Matrix_MultV(Vector_T VRes, Matrix_T M, Vector_T V)
/************************************************************************/
/*                                                                      */
/*                          ----   =   -                                */
/* 			    VRes = M * V				*/
/*                                                                      */
/************************************************************************/
{
    int		i;

    for (i = 0; i < 3; i++) 
    	VRes[i] = V[0] * M[i][0] +
    	          V[1] * M[i][1] +
    	          V[2] * M[i][2];

} /* Matrix_MultV */

void Compute_Rotation_Matrix(Matrix_T R_Matrix, Vector_T Rot) /* Degrees */
/************************************************************************/
/*                                                                      */
/* Precompute 3d rotation matrix for a rotation of 'Rot[0]' degrees 	*/
/* around the X-axis, 'Rot[1]' degrees around the Y-axis and 'Rot[2]' 	*/
/* degrees around the Z-axis.						*/
/*									*/
/************************************************************************/
{
    Matrix_T	M_X, M_Y, M_Z, M_Tmp;
    double	Rot_X, Rot_Y, Rot_Z;
    double	S, C;

    Rot_X = PI * Rot[0] / 180.0;
    Rot_Y = PI * Rot[1] / 180.0;
    Rot_Z = PI * Rot[2] / 180.0;

    S = sin(Rot_X);
    C = cos(Rot_X);
    M_X[0][0] = 1.0; M_X[1][0] = 0.0; M_X[2][0] = 0.0;
    M_X[0][1] = 0.0; M_X[1][1] = C;   M_X[2][1] = S;
    M_X[0][2] = 0.0; M_X[1][2] = -S;  M_X[2][2] = C;

    S = sin(Rot_Y);
    C = cos(Rot_Y);
    M_Y[0][0] = C;   M_Y[1][0] = 0.0; M_Y[2][0] = -S;
    M_Y[0][1] = 0.0; M_Y[1][1] = 1.0; M_Y[2][1] = 0.0;
    M_Y[0][2] = S;   M_Y[1][2] = 0.0; M_Y[2][2] = C;

    S = sin(Rot_Z);
    C = cos(Rot_Z);
    M_Z[0][0] = C;   M_Z[1][0] = S;   M_Z[2][0] = 0.0;
    M_Z[0][1] = -S;  M_Z[1][1] = C;   M_Z[2][1] = 0.0;
    M_Z[0][2] = 0.0; M_Z[1][2] = 0.0; M_Z[2][2] = 1.0;

    Matrix_Mult(M_Tmp, M_X,  M_Y);
    Matrix_Mult(R_Matrix, M_Tmp, M_Z);

} /* Compute_Rotation_Matrix */

void Compute_Inverse_Rotation_Matrix(Matrix_T R_Matrix, Vector_T Rot)
/************************************************************************/
/*                                                                      */
/* Precompute inverse 3d rotation matrix for a rotation of 'Rot[0]' 	*/
/* degrees around the X-axis, 'Rot[1]' degrees around the Y-axis and 	*/
/* 'Rot[2]' degrees around the Z-axis.					*/
/*									*/
/************************************************************************/
{
    Matrix_T	M_X, M_Y, M_Z, M_Tmp;
    double	Rot_X, Rot_Y, Rot_Z;
    double	S, C;

    Rot_X = PI * Rot[0] / 180.0;
    Rot_Y = PI * Rot[1] / 180.0;
    Rot_Z = PI * Rot[2] / 180.0;

    S = sin(-Rot_X);
    C = cos(-Rot_X);
    M_X[0][0] = 1.0; M_X[1][0] = 0.0; M_X[2][0] = 0.0;
    M_X[0][1] = 0.0; M_X[1][1] = C;   M_X[2][1] = S;
    M_X[0][2] = 0.0; M_X[1][2] = -S;  M_X[2][2] = C;

    S = sin(-Rot_Y);
    C = cos(-Rot_Y);
    M_Y[0][0] = C;   M_Y[1][0] = 0.0; M_Y[2][0] = -S;
    M_Y[0][1] = 0.0; M_Y[1][1] = 1.0; M_Y[2][1] = 0.0;
    M_Y[0][2] = S;   M_Y[1][2] = 0.0; M_Y[2][2] = C;

    S = sin(-Rot_Z);
    C = cos(-Rot_Z);
    M_Z[0][0] = C;   M_Z[1][0] = S;   M_Z[2][0] = 0.0;
    M_Z[0][1] = -S;  M_Z[1][1] = C;   M_Z[2][1] = 0.0;
    M_Z[0][2] = 0.0; M_Z[1][2] = 0.0; M_Z[2][2] = 1.0;

    Matrix_Mult(M_Tmp, M_Z,  M_Y);
    Matrix_Mult(R_Matrix, M_Tmp, M_X);

} /* Compute_Inverse_Rotation_Matrix */

void Precompute_Kochanek_Bartels(Matrix4_T Mres, 
				 Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Precompute matrix used for Kochanek-Bartels spline interpolation.	*/
/* See Image Synthesis p. 36 - 38.					*/
/*                                                                      */
/************************************************************************/
{
    Matrix4_T	M_Tmp;
    double	t, b, c, tm, bm, cm, bp, cp;
    Knot_T	*Knot0, *Knot1, *Knot2, *Knot3;
    short	PId0, PId1, PId2, PId3;
    short	Nbr_Knots;

    Nbr_Knots	= Spline->Nbr_Knots;

		/* The knot before this knot */

    Knot0 = Knot->Previous;
    if (Knot0 == NULL) Knot0 = Spline->Last; 	/* Loop */
    
		/* This knot */

    Knot1 = Knot;

		/* The knot after this knot */

    Knot2 = Knot->Next;
    if (Knot2 == NULL) Knot2 = Spline->First; /* Loop */

		/* The second knot after this knot */
	
    Knot3 = Knot2->Next;
    if (Knot3 == NULL) Knot3 = Spline->First; /* Loop */

    PId0 = Knot0->Point_Id;
    PId1 = Knot1->Point_Id;
    PId2 = Knot2->Point_Id;
    PId3 = Knot3->Point_Id;

    M_Tmp[0][0] = Points[PId1].Pos[0];
    M_Tmp[0][1] = Points[PId1].Pos[1];
    M_Tmp[0][2] = Points[PId1].Pos[2];
    M_Tmp[0][3] = 0.0;

    M_Tmp[1][0] = Points[PId2].Pos[0];
    M_Tmp[1][1] = Points[PId2].Pos[1];
    M_Tmp[1][2] = Points[PId2].Pos[2];
    M_Tmp[1][3] = 0.0;

    t = Knot1->Tension;	  tm = 1.0 - t;
    b = Knot1->Bias;	  bm = 1.0 - b;  bp = 1.0 + b;
    c = Knot1->Continuity;  cm = 1.0 - c;  cp = 1.0 + c;

    if (Knot != Spline->First || Spline->Loop) {

	M_Tmp[2][0] = 0.5 * tm * (
	 	bm * cm * (Points[PId2].Pos[0] - Points[PId1].Pos[0]) +
		bp * cp * (Points[PId1].Pos[0] - Points[PId0].Pos[0])
	);
	M_Tmp[2][1] = 0.5 * tm * (
		bm * cm * (Points[PId2].Pos[1] - Points[PId1].Pos[1]) +
		bp * cp * (Points[PId1].Pos[1] - Points[PId0].Pos[1])
	);
	M_Tmp[2][2] = 0.5 * tm * (
	    	bm * cm * (Points[PId2].Pos[2] - Points[PId1].Pos[2]) +
	    	bp * cp * (Points[PId1].Pos[2] - Points[PId0].Pos[2])
	);

	M_Tmp[2][3] = 0.0;

    } else {

	M_Tmp[2][0] = 0.0;
	M_Tmp[2][1] = 0.0;
	M_Tmp[2][2] = 0.0;
	M_Tmp[2][3] = 0.0;

    } /* if .. else .. */

    t = Knot2->Tension;	  tm = 1.0 - t;
    b = Knot2->Bias;	  bm = 1.0 - b;  bp = 1.0 + b;
    c = Knot2->Continuity;  cm = 1.0 - c;  cp = 1.0 + c;

    if (Knot->Next != NULL && Knot->Next->Next != NULL) {

	M_Tmp[3][0] = 0.5 * tm * (
		bm * cp * (Points[PId3].Pos[0] - Points[PId2].Pos[0]) +
		bp * cm * (Points[PId2].Pos[0] - Points[PId1].Pos[0])
	);
	M_Tmp[3][1] = 0.5 * tm * (
		bm * cp * (Points[PId3].Pos[1] - Points[PId2].Pos[1]) +
		bp * cm * (Points[PId2].Pos[1] - Points[PId1].Pos[1])
	);
	M_Tmp[3][2] = 0.5 * tm * (
		bm * cp * (Points[PId3].Pos[2] - Points[PId2].Pos[2]) +
		bp * cm * (Points[PId2].Pos[2] - Points[PId1].Pos[2])
	);
	M_Tmp[3][3] = 0.0;

    } else {

	M_Tmp[3][0] = 0.0;
	M_Tmp[3][1] = 0.0;
	M_Tmp[3][2] = 0.0;
	M_Tmp[3][3] = 0.0;

    } /* if .. else .. */


    Matrix4_Mult(Mres, M_Hermite, M_Tmp);

} /* Precompute_Kochanek_Bartels */

void Compute_Position(Vector4_T Vres, double T, Matrix4_T M_Precomp)
/************************************************************************/
/*                                                                      */
/* Compute position on a Kochanek Bartels spline at parameter value T	*/
/* using the precomputed matrix M_Precomp.				*/
/* Return the result in Vres						*/
/*                                                                      */
/************************************************************************/
{
    Vector4_T	Vt;

    Vt[3] = 1.0;
    Vt[2] = T;
    Vt[1] = T * T;
    Vt[0] = Vt[1] * T;

    Matrix4_VMult(Vres, Vt, M_Precomp);

} /* Compute_Position */
