/****************************************************************************
*                matrices.c
*
*  This module contains code to manipulate 4x4 matrices.
*
*  from Persistence of Vision Raytracer
*  Copyright 1993 Persistence of Vision Team
*---------------------------------------------------------------------------
*  NOTICE: This source code file is provided so that users may experiment
*  with enhancements to POV-Ray and to port the software to platforms other 
*  than those supported by the POV-Ray Team.  There are strict rules under
*  which you are permitted to use this file.  The rules are in the file
*  named POVLEGAL.DOC which should be distributed with this file. If 
*  POVLEGAL.DOC is not available or for more info please contact the POV-Ray
*  Team Coordinator by leaving a message in CompuServe's Graphics Developer's
*  Forum.  The latest version of POV-Ray may be found there as well.
*
* This program is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
*
*****************************************************************************/

#include "frame.h"
#include "vector.h"
#include "povproto.h"

void MZero (result)
MATRIX *result;
  {
  /* Initialize the matrix to the following values:
   0.0   0.0   0.0   0.0
   0.0   0.0   0.0   0.0
   0.0   0.0   0.0   0.0
   0.0   0.0   0.0   0.0
*/
  register int i, j;

  for (i = 0 ; i < 4 ; i++)
    for (j = 0 ; j < 4 ; j++)
    (*result)[i][j] = 0.0;
  }

void MIdentity (result)
MATRIX *result;
  {
  /* Initialize the matrix to the following values:
   1.0   0.0   0.0   0.0
   0.0   1.0   0.0   0.0
   0.0   0.0   1.0   0.0
   0.0   0.0   0.0   1.0
*/
  register int i, j;

  for (i = 0 ; i < 4 ; i++)
    for (j = 0 ; j < 4 ; j++)
    if (i == j)
      (*result)[i][j] = 1.0;
    else
      (*result)[i][j] = 0.0;
  }

void MTimes (result, matrix1, matrix2)
MATRIX *result, *matrix1, *matrix2;
  {
  register int i, j, k;
  MATRIX temp_matrix;

  for (i = 0 ; i < 4 ; i++)
    for (j = 0 ; j < 4 ; j++) 
    {
    temp_matrix[i][j] = 0.0;
    for (k = 0 ; k < 4 ; k++)
      temp_matrix[i][j] += (*matrix1)[i][k] * (*matrix2)[k][j];
    }

  for (i = 0 ; i < 4 ; i++)
    for (j = 0 ; j < 4 ; j++)
    (*result)[i][j] = temp_matrix[i][j];
  }

/*  AAC - These are not used, so they are commented out to save code space...

void MAdd (result, matrix1, matrix2)
   MATRIX *result, *matrix1, *matrix2;
   {
   register int i, j;

   for (i = 0 ; i < 4 ; i++)
      for (j = 0 ; j < 4 ; j++)
         (*result)[i][j] = (*matrix1)[i][j] + (*matrix2)[i][j];
   }

void MSub (result, matrix1, matrix2)
   MATRIX *result, *matrix1, *matrix2;
   {
   register int i, j;

   for (i = 0 ; i < 4 ; i++)
      for (j = 0 ; j < 4 ; j++)
         (*result)[i][j] = (*matrix1)[i][j] - (*matrix2)[i][j];
   }

void MScale (result, matrix1, amount)
MATRIX *result, *matrix1;
DBL amount;
{
   register int i, j;

   for (i = 0 ; i < 4 ; i++)
      for (j = 0 ; j < 4 ; j++)
	 if (amount == 1.0)
	    (*result)[i][j] = (*matrix1)[i][j]; * just copy *
	 else
            (*result)[i][j] = (*matrix1)[i][j] * amount;
   return;
}
... up to here! */

void MTranspose (result, matrix1)
MATRIX *result, *matrix1;
  {
  register int i, j;
  MATRIX temp_matrix;

  for (i = 0 ; i < 4 ; i++)
    for (j = 0 ; j < 4 ; j++)
    temp_matrix[i][j] = (*matrix1)[j][i];

  for (i = 0 ; i < 4 ; i++)
    for (j = 0 ; j < 4 ; j++)
    (*result)[i][j] = temp_matrix[i][j];
  }


/* 16/10/1993 : V 1.32 : Marc Abramson d'apres PH. Lafargues	*/
void MTransPoint (result, vector, transform)
VECTOR *result, *vector;
TRANSFORM *transform;
  {
   MATRIX *matrix;
	DBL	x,y,z;

	matrix = &(transform -> matrix);

	x  =	(vector -> x * (*matrix)[0][0])
		+	(vector -> y * (*matrix)[1][0])
		+	(vector -> z * (*matrix)[2][0])
		+				   (*matrix)[3][0];
	y  =	(vector -> x * (*matrix)[0][1])
		+	(vector -> y * (*matrix)[1][1])
		+	(vector -> z * (*matrix)[2][1])
		+				   (*matrix)[3][1];
	z  = 	(vector -> x * (*matrix)[0][2])
		+	(vector -> y * (*matrix)[1][2])
		+	(vector -> z * (*matrix)[2][2])
		+				   (*matrix)[3][2];
	result->x=x;
	result->y=y;
	result->z=z;

   }

/* 16/10/1993 : V 1.32 : Marc Abramson d'apres PH. Lafargues	*/
void MInvTransPoint (result, vector, transform)
VECTOR *result, *vector;
TRANSFORM *transform;
  {
  	MATRIX *matrix;
	DBL	x,y,z;

	matrix = &(transform -> inverse);

	x	=	vector -> x * (*matrix)[0][0]
		+	vector -> y * (*matrix)[1][0]
		+	vector -> z * (*matrix)[2][0]
		+				  (*matrix)[3][0];
	y	=	vector -> x * (*matrix)[0][1]
		+	vector -> y * (*matrix)[1][1]
		+	vector -> z * (*matrix)[2][1]
		+				  (*matrix)[3][1];
	z	=	vector -> x * (*matrix)[0][2]
		+	vector -> y * (*matrix)[1][2]
		+	vector -> z * (*matrix)[2][2]
		+				  (*matrix)[3][2];
	result->x=x;
	result->y=y;
	result->z=z;
  }


/* 16/10/1993 : V 1.32 : Marc Abramson d'apres PH. Lafargues	*/
void MTransDirection (result, vector, transform)
VECTOR *result, *vector;
TRANSFORM *transform;
  {
   MATRIX *matrix;
	DBL	x,y,z;

	matrix = &(transform -> matrix);

	x  =	(vector -> x * (*matrix)[0][0])
		+	(vector -> y * (*matrix)[1][0])
		+	(vector -> z * (*matrix)[2][0]);
	y  =	(vector -> x * (*matrix)[0][1])
		+	(vector -> y * (*matrix)[1][1])
		+	(vector -> z * (*matrix)[2][1]);
	z  = 	(vector -> x * (*matrix)[0][2])
		+	(vector -> y * (*matrix)[1][2])
		+	(vector -> z * (*matrix)[2][2]);
	result->x=x;
	result->y=y;
	result->z=z;
  }

/* 16/10/1993 : V 1.32 : Marc Abramson d'apres PH. Lafargues	*/
void MInvTransDirection (result, vector, transform)
VECTOR *result, *vector;
TRANSFORM *transform;
  {
 	MATRIX *matrix;
	DBL	x,y,z;

	matrix = &(transform -> inverse);

	x	=	vector -> x * (*matrix)[0][0]
		+	vector -> y * (*matrix)[1][0]
		+	vector -> z * (*matrix)[2][0];
	y	=	vector -> x * (*matrix)[0][1]
		+	vector -> y * (*matrix)[1][1]
		+	vector -> z * (*matrix)[2][1];
	z	=	vector -> x * (*matrix)[0][2]
		+	vector -> y * (*matrix)[1][2]
		+	vector -> z * (*matrix)[2][2];
	result->x=x;
	result->y=y;
	result->z=z;
  }


/* 16/10/1993 : V 1.32 : Marc Abramson d'apres PH. Lafargues	*/
void MTransNormal (result, vector, transformation)
VECTOR *result, *vector;
TRANSFORM *transformation;
{	register MATRIX *matrix;
	DBL	x,y,z;

   matrix = &(transformation -> inverse);

	x	=	vector -> x * (*matrix)[0][0]
		+	vector -> y * (*matrix)[0][1]
		+	vector -> z * (*matrix)[0][2];
	y	=	vector -> x * (*matrix)[1][0]
		+	vector -> y * (*matrix)[1][1]
		+	vector -> z * (*matrix)[1][2];
	z	=	vector -> x * (*matrix)[2][0]
		+	vector -> y * (*matrix)[2][1]
		+	vector -> z * (*matrix)[2][2];
	result->x=x;
	result->y=y;
	result->z=z;
}

/* 16/10/1993 : V 1.32 : Marc Abramson d'apres PH. Lafargues	*/
void MInvTransNormal (result, vector, transform)
VECTOR *result, *vector;
TRANSFORM *transform;
  {
   MATRIX *matrix;
	DBL	x,y,z;

	matrix = &(transform -> matrix);

	x  =	(vector -> x * (*matrix)[0][0])
		+	(vector -> y * (*matrix)[0][1])
		+	(vector -> z * (*matrix)[0][2]);
	y  =	(vector -> x * (*matrix)[1][0])
		+	(vector -> y * (*matrix)[1][1])
		+	(vector -> z * (*matrix)[1][2]);
	z  = 	(vector -> x * (*matrix)[2][0])
		+	(vector -> y * (*matrix)[2][1])
		+	(vector -> z * (*matrix)[2][2]);
	result->x=x;
	result->y=y;
	result->z=z;
}

void Compute_Scaling_Transform (result, vector)
TRANSFORM *result;
VECTOR *vector;
  {
  MIdentity ((MATRIX *)result -> matrix);
  (result -> matrix)[0][0] = vector -> x;
  (result -> matrix)[1][1] = vector -> y;
  (result -> matrix)[2][2] = vector -> z;

  MIdentity ((MATRIX *)result -> inverse);
  (result -> inverse)[0][0] = 1.0 / vector -> x;
  (result -> inverse)[1][1]= 1.0 / vector -> y;
  (result -> inverse)[2][2] = 1.0 / vector -> z;
  }

/* AAC - This is not used, so it's commented out...

void Compute_Inversion_Transform (result)
   TRANSFORM *result;
   {
   MIdentity ((MATRIX *)result -> matrix);
   (result -> matrix)[0][0] = -1.0;
   (result -> matrix)[1][1] = -1.0;
   (result -> matrix)[2][2] = -1.0;
   (result -> matrix)[3][3] = -1.0;


   (result -> inverse)[0][0] = -1.0;
   (result -> inverse)[1][1] = -1.0;
   (result -> inverse)[2][2] = -1.0;
   (result -> inverse)[3][3] = -1.0;
   }
... up to here! */

void Compute_Translation_Transform (transform, vector)
TRANSFORM *transform;
VECTOR *vector;
  {
  MIdentity ((MATRIX *)transform -> matrix);
  (transform -> matrix)[3][0] = vector -> x;
  (transform -> matrix)[3][1] = vector -> y;
  (transform -> matrix)[3][2] = vector -> z;

  MIdentity ((MATRIX *)transform -> inverse);
  (transform -> inverse)[3][0] = 0.0 - vector -> x;
  (transform -> inverse)[3][1] = 0.0 - vector -> y;
  (transform -> inverse)[3][2] = 0.0 - vector -> z;
  }

void Compute_Rotation_Transform (transform, vector)
TRANSFORM *transform;
VECTOR *vector;
  {
  MATRIX Matrix;
  VECTOR Radian_Vector;
  register DBL cosx, cosy, cosz, sinx, siny, sinz;

  VScale (Radian_Vector, *vector, M_PI/180.0);
  MIdentity ((MATRIX *)transform -> matrix);
  cosx = cos (Radian_Vector.x);
  sinx = sin (Radian_Vector.x);
  cosy = cos (Radian_Vector.y);
  siny = sin (Radian_Vector.y);
  cosz = cos (Radian_Vector.z);
  sinz = sin (Radian_Vector.z);

  (transform -> matrix) [1][1] = cosx;
  (transform -> matrix) [2][2] = cosx;
  (transform -> matrix) [1][2] = sinx;
  (transform -> matrix) [2][1] = 0.0 - sinx;
  MTranspose ((MATRIX *)transform -> inverse, (MATRIX *)transform -> matrix);

  MIdentity ((MATRIX *)Matrix);
  Matrix [0][0] = cosy;
  Matrix [2][2] = cosy;
  Matrix [0][2] = 0.0 - siny;
  Matrix [2][0] = siny;
  MTimes ((MATRIX *)transform -> matrix, (MATRIX *)transform -> matrix, (MATRIX *)Matrix);
  MTranspose ((MATRIX *)Matrix, (MATRIX *)Matrix);
  MTimes ((MATRIX *)transform -> inverse, (MATRIX *)Matrix, (MATRIX *)transform -> inverse);

  MIdentity ((MATRIX *)Matrix);
  Matrix [0][0] = cosz;
  Matrix [1][1] = cosz;
  Matrix [0][1] = sinz;
  Matrix [1][0] = 0.0 - sinz;
  MTimes ((MATRIX *)transform -> matrix, (MATRIX *)transform -> matrix, (MATRIX *)Matrix);
  MTranspose ((MATRIX *)Matrix, (MATRIX *)Matrix);
  MTimes ((MATRIX *)transform -> inverse, (MATRIX *)Matrix, (MATRIX *)transform -> inverse);
  }

/* AAC - This is not used so it's commented out...

void Compute_Look_At_Transform (result, Look_At, Up, Right)
   TRANSFORM *result;
   VECTOR *Look_At, *Up, *Right;
   {
   MIdentity ((MATRIX *)result -> inverse);
   (result -> matrix)[0][0] = Right->x;
   (result -> matrix)[0][1] = Right->y;
   (result -> matrix)[0][2] = Right->z;
   (result -> matrix)[1][0] = Up->x;
   (result -> matrix)[1][1] = Up->y;
   (result -> matrix)[1][2] = Up->z;
   (result -> matrix)[2][0] = Look_At->x;
   (result -> matrix)[2][1] = Look_At->y;
   (result -> matrix)[2][2] = Look_At->z;

   MIdentity ((MATRIX *)result -> matrix);
   MTranspose ((MATRIX *)result -> matrix, (MATRIX *)result -> inverse);   
   }

... up to here! */

void Compose_Transforms (Original_Transform, New_Transform)
TRANSFORM *Original_Transform, *New_Transform;
  {
  MTimes ((MATRIX *)Original_Transform -> matrix,
    (MATRIX *)Original_Transform -> matrix,
    (MATRIX *)New_Transform -> matrix);

  MTimes ((MATRIX *)Original_Transform -> inverse,
    (MATRIX *)New_Transform -> inverse,
    (MATRIX *)Original_Transform -> inverse);
  }

/* Rotation about an arbitrary axis - formula from:
      "Computational Geometry for Design and Manufacture",
      Faux & Pratt
   Note that the angles for this transform are specified in radians.
*/
void Compute_Axis_Rotation_Transform (transform, V, angle)
TRANSFORM *transform;
VECTOR *V;
DBL angle;
  {
  DBL l, cosx, sinx;

  VLength(l, *V);
  VInverseScaleEq(*V, l);

  MIdentity(&transform->matrix);
  cosx = cos(angle);
  sinx = sin(angle);
  transform->matrix[0][0] = V->x * V->x + cosx * (1.0 - V->x * V->x);
  transform->matrix[0][1] = V->x * V->y * (1.0 - cosx) + V->z * sinx;
  transform->matrix[0][2] = V->x * V->z * (1.0 - cosx) - V->y * sinx;
  transform->matrix[1][0] = V->x * V->y * (1.0 - cosx) - V->z * sinx;
  transform->matrix[1][1] = V->y * V->y + cosx * (1.0 - V->y * V->y);
  transform->matrix[1][2] = V->y * V->z * (1.0 - cosx) + V->x * sinx;
  transform->matrix[2][0] = V->x * V->z * (1.0 - cosx) + V->y * sinx;
  transform->matrix[2][1] = V->y * V->z * (1.0 - cosx) - V->x * sinx;
  transform->matrix[2][2] = V->z * V->z + cosx * (1.0 - V->z * V->z);
  MTranspose(&transform->inverse, &transform->matrix);   
  }

/* Given a point and a direction and a radius, find the transform
   that brings these into a canonical coordinate system */
void
Compute_Coordinate_Transform(trans, origin, up, radius, length)
TRANSFORM *trans;
VECTOR *origin;
VECTOR *up;
DBL radius;
DBL length;
  {
  TRANSFORM trans2;
  VECTOR tmpv;

  Make_Vector(&tmpv, radius, radius, length);
  Compute_Scaling_Transform(trans, &tmpv);
  if (fabs(up->z) == 1.0)
    Make_Vector(&tmpv, 1.0, 0.0, 0.0)
else
  Make_Vector(&tmpv, -up->y, up->x, 0.0)
    Compute_Axis_Rotation_Transform(&trans2, &tmpv, acos(up->z));
Compose_Transforms(trans, &trans2);
Compute_Translation_Transform(&trans2, origin);
Compose_Transforms(trans, &trans2);
}

TRANSFORM *Create_Transform()
{
TRANSFORM *New;

if ((New = (TRANSFORM *) malloc (sizeof (TRANSFORM))) == NULL)
MAError ("transform");

MIdentity ((MATRIX *) &(New -> matrix[0][0]));
MIdentity ((MATRIX *) &(New -> inverse[0][0]));
return (New);
}

TRANSFORM *Copy_Transform (Old)
TRANSFORM *Old;
{
TRANSFORM *New;
if (Old != NULL)
  {
  New  = Create_Transform ();
  *New = *Old;
  }
else New = NULL;
return (New);
}

VECTOR *Create_Vector ()
{
VECTOR *New;

if ((New = (VECTOR *) malloc (sizeof (VECTOR))) == NULL)
MAError ("vector");

Make_Vector (New, 0.0, 0.0, 0.0);

return (New);
}

VECTOR *Copy_Vector (Old)
VECTOR *Old;
{
VECTOR *New;
if (Old != NULL)
  {
  New  = Create_Vector ();
  *New = *Old;
  }
else New = NULL;
return (New);
}

DBL *Create_Float ()
{
DBL *New_Float;

if ((New_Float = (DBL *) malloc (sizeof (DBL))) == NULL)
MAError ("float");

*New_Float = 0.0;
return (New_Float);
}

DBL *Copy_Float (Old)
DBL *Old;
{
DBL *New;
if (Old)
  {
  New  = Create_Float ();
  *New = *Old;
  }
else New = NULL;
return (New);
}

