/* #include "HEADERS.h" */
/* Copyright 1988, Brown Computer Graphics Group.  All Rights Reserved. */

/* --------------------------------------------------------------------------
 * This file contains routines that perform geometry-related operations
 * on matrices.
 * -------------------------------------------------------------------------*/

#include <Math/mat3defs.h>

/* --------------------------  Static Routines	---------------------------- */

/* -------------------------  Internal Routines  --------------------------- */

/* --------------------------  Public Routines	---------------------------- */

/*
 * This takes a matrix used to transform points, and returns a corresponding
 * matrix that can be used to transform direction vectors (between points).
 */

void
MAT3direction_matrix(register double (*result_mat)[4], register double (*mat)[4])
{
   register int i;

   MAT3copy(result_mat, mat);

   for (i = 0; i < 4; i++) result_mat[i][3] = result_mat[3][i] = 0.0;

   result_mat[3][3] = 1.0;
}

/*
 * This takes a matrix used to transform points, and returns a corresponding
 * matrix that can be used to transform vectors that must remain perpendicular
 * to planes defined by the points.  It is useful when you are transforming
 * some object that has both points and normals in its definition, and you
 * only have the transformation matrix for the points.	This routine returns
 * FALSE if the normal matrix is uncomputable.	Otherwise, it returns TRUE.
 *
 * Spike sez: "This is the adjoint for the non-homogeneous part of the
 *	       transformation."
 */

int
MAT3normal_matrix(register double (*result_mat)[4], register double (*mat)[4])
{
   register int ret;
   MAT3mat	tmp_mat;

   MAT3direction_matrix(result_mat, mat);

   if ( (ret = MAT3invert(tmp_mat, tmp_mat)) ) {
       MAT3transpose(result_mat, tmp_mat);
   }

   return(ret);
}

/*
 * Sets the given matrix to be a scale matrix for the given vector of
 * scale values.
 */

void
MAT3scale(double (*result_mat)[4], double *scale)
{
   MAT3identity(result_mat);

   result_mat[0][0] = scale[0];
   result_mat[1][1] = scale[1];
   result_mat[2][2] = scale[2];
}

/*
 * Sets up a matrix for a rotation about an axis given by the line from
 * (0,0,0) to axis, through an angle (in radians).
 * Looking along the axis toward the origin, the rotation is counter-clockwise.
 */

#define SELECT	.7071	/* selection constant (roughly .5*sqrt(2) */

void
MAT3rotate(double (*result_mat)[4], double *axis, double angle_in_radians)
{
   MAT3vec	naxis,	/* Axis of rotation, normalized 		*/
		base2,	/* 2nd unit basis vec, perp to axis		*/
		base3;	/* 3rd unit basis vec, perp to axis & base2	*/
   double	dot;
   MAT3mat	base_mat,	/* Change-of-basis matrix		*/
		base_mat_trans; /* Inverse of c-o-b matrix		*/
   register int i;

   /* Step 1: extend { axis } to a basis for 3-space: { axis, base2, base3 }
    * which is orthonormal (all three have unit length, and all three are
    * mutually orthogonal). Also should be oriented, i.e. axis cross base2 =
    * base3, rather than -base3.
    *
    * Method: Find a vector linearly independent from axis. For this we
    * either use the y-axis, or, if that is too close to axis, the
    * z-axis. 'Too close' means that the dot product is too near to 1.
    */

   MAT3_COPY_VEC(naxis, axis);
   MAT3_NORMALIZE_VEC(naxis, dot);

   if (dot == 0.0) {
       /* ERR_ERROR(MAT3_errid, ERR_SEVERE,
		   (ERR_S, "Zero-length axis vector given to MAT3rotate")); */
      return;
   }

   MAT3perp_vec(base2, naxis, TRUE);
   MAT3cross_product(base3, naxis, base2);

   /* Set up the change-of-basis matrix, and its inverse */
   MAT3identity(base_mat);
   MAT3identity(base_mat_trans);
   MAT3identity(result_mat);

   for (i = 0; i < 3; i++){
      base_mat_trans[i][0] = base_mat[0][i] = naxis[i];
      base_mat_trans[i][1] = base_mat[1][i] = base2[i];
      base_mat_trans[i][2] = base_mat[2][i] = base3[i];
   }

   /* If T(u) = uR, where R is base_mat, then T(x-axis) = naxis,
    * T(y-axis) = base2, and T(z-axis) = base3. The inverse of base_mat is
    * its transpose.  OK?
    */

   result_mat[1][1] =	result_mat[2][2] = cos(angle_in_radians);
   result_mat[2][1] = -(result_mat[1][2] = sin(angle_in_radians));

   MAT3mult(result_mat, base_mat_trans, result_mat);
   MAT3mult(result_mat, result_mat,	base_mat);
}

/*
 * Sets the given matrix to be a translation matrix for the given vector of
 * translation values.
 */

void
MAT3translate(double (*result_mat)[4], double *trans)
{
   MAT3identity(result_mat);

   result_mat[3][0] = trans[0];
   result_mat[3][1] = trans[1];
   result_mat[3][2] = trans[2];
}

/*
 * Sets the given matrix to be a shear matrix for the given x and y shear
 * values.
 */

void
MAT3shear(double (*result_mat)[4], double xshear, double yshear)
{
   MAT3identity(result_mat);

   result_mat[2][0] = xshear;
   result_mat[2][1] = yshear;
}