 /*
  * Khoros: $Id: matrix_3D.c,v 1.2 1992/03/20 22:46:36 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: matrix_3D.c,v 1.2 1992/03/20 22:46:36 dkhoros Exp $";
#endif

 /*
  * $Log: matrix_3D.c,v $
 * Revision 1.2  1992/03/20  22:46:36  dkhoros
 * VirtualPatch5
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "X3D.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>             file name: matrix_3D.c                    <<<<
   >>>>                                                       <<<<
   >>>>              3D Matrix  Utilities                     <<<<
   >>>>                                                       <<<<
   >>>>              _X3D_clear_matrix()                      <<<<
   >>>>              _X3D_matrix_set_identity()               <<<<
   >>>>              _X3D_matrix_set_viewing()                <<<<
   >>>>              _X3D_matrix_set_parallel()               <<<<
   >>>>              _X3D_matrix_set_perspective()            <<<<
   >>>>              _X3D_matrix_mult()               	      <<<<
   >>>>              _X3D_matrix_set_ndc()            	      <<<<
   >>>>              _X3D_write_matrix()            	      <<<<
   >>>>              _X3D_matrix_set_dc()            	      <<<<
   >>>>              X3D_matrix_set_translate()       	      <<<<
   >>>>              X3D_matrix_set_scale()       	      <<<<
   >>>>              X3D_matrix_set_rotate()       	      <<<<
   >>>>              X3D_matrix_set_inverse()       	      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



/************************************************************
*
*  MODULE NAME:   _X3D_clear_matrix
*
*      PURPOSE:   Set a 4x4 matrix to ALL zeros.
*
*
*        INPUT:   1) matrix -- a 4x4 Real array.
*
*
*       OUTPUT:   none
*
*    CALLED BY:   many routines
*
*   WRITTEN BY:   Mark Young
*
*
*************************************************************/


_X3D_clear_matrix (matrix)

Matrix  matrix;
{
	int i,j;

	for (i = 0; i < 4; i++)
	{
	    for (j = 0; j < 4; j++)
	    {
		matrix[i][j] = 0.0;
	    }
	}
}


/************************************************************
*
*  MODULE NAME:   _X3D_matrix_set_identity
*
*      PURPOSE:   Set a 4x4 matrix to identity matrix.
*
*
*        INPUT:   1) matrix -- a 4x4 Real array.
*
*
*       OUTPUT:   none
*
*    CALLED BY:   many routines
*
*   WRITTEN BY:   Mark Young
*
*
*************************************************************/


_X3D_matrix_set_identity (matrix)

Matrix  matrix;
{
	_X3D_clear_matrix(matrix);
	matrix[0][0] = matrix[1][1] = 
	matrix[2][2] = matrix[3][3] = 1.0;
}


/************************************************************
*
*  MODULE NAME:  _X3D_matrix_set_viewing
*
*      PURPOSE:  Set a viewing matrix
*
*
*        INPUT:  1) theta -- a Real representing the x-y axis offset.
*		 2) alpha -- a Real representing the z-y axis offset.
*		 3) eye_distance -- a Real representing the WC distance from 
*		 	eye to the origin.
*
*
*       OUTPUT:  1) a 4x4 Real matrix.
*
*    CALLED BY:
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/


_X3D_matrix_set_viewing (theta, alpha, eye_distance, matrix)

Real	theta, alpha, eye_distance;
Matrix	matrix;
{

	theta = _X3D_DegToRads(theta);
	alpha = _X3D_DegToRads(alpha);

	_X3D_clear_matrix(matrix);
	matrix[0][0] =  cos(theta);
	matrix[0][1] = -(cos(alpha) * sin(theta));
	matrix[0][2] = -(sin(alpha) * cos(theta));
	matrix[1][0] =  sin(theta);
	matrix[1][1] =  cos(alpha) * cos(theta);
	matrix[1][2] = -(sin(alpha) * cos(theta));
	matrix[2][1] =  sin(alpha);
	matrix[2][2] = -(cos(alpha));
	matrix[3][2] =  eye_distance;
	matrix[3][3] =  1.0;
}


/************************************************************
*
*  MODULE NAME:  _X3D_matrix_set_parallel
*
*      PURPOSE:  Sets up a parallel transformation matrix.
*
*
*        INPUT:  1)  Type of parallel view.
*
*
*       OUTPUT:  1)  4x4 parallel transformation matrix.
*
*    CALLED BY:
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/




_X3D_matrix_set_parallel (projection, matrix)

int	projection;
Real	matrix[4][4];
{
	Real  length, beta;

	/*  The z-axis is projected to a 2D space.  The axis
	     extends at a specified angle, between the x & y axes
	     (a left handed system).  */

	switch (projection)
	{
	    case Orthographic:	length = 0.0; beta = 90.0;
				break;

	    case Cavalier:	length = 1.0; beta = 45.0;
				break;

	    case Cabinet:	length = 0.5; beta = 63.4;
				break;
	}

	_X3D_matrix_set_identity(matrix);

	matrix[2][2] = 0.0;
	beta = _X3D_DegToRads(beta);
	matrix[2][0] = length*cos(beta); matrix[2][1] = length*sin(beta);
}





/************************************************************
*
*  MODULE NAME:  _X3D_matrix_set_perspective
*
*      PURPOSE:  Set a 4x4 matrix to a perspective viewing matrix.
*
*
*        INPUT:  1)  view_distance -- distance from the eye to the viewport
*
*
*       OUTPUT:  1)  the resultant matrix
*
*    CALLED BY:
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/


_X3D_matrix_set_perspective(view_distance, matrix)

Real	view_distance;
Matrix  matrix;
{
	_X3D_matrix_set_identity(matrix);
	matrix[2][2] = 0.0;

	if (view_distance == 0.0)
	   matrix[2][3] = XV_MAXFLOAT;
	else
	   matrix[2][3] = 1.0/view_distance;
}




/************************************************************
*
*  MODULE NAME: _X3D_matrix_mult
*
*      PURPOSE: multiplies 2 4x4 matrices
*
*        INPUT: 2 4x4 matrices
*
*       OUTPUT: the resulting 4x4 matrix
*              
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


_X3D_matrix_mult (matrix1, matrix2, outmatrix)

Matrix matrix1, matrix2, outmatrix;
{
	int   i, j;
	Matrix tmp_matrix;

	/*
	 *  Multiply the two matrices together and return the result in
	 *  the out matrix, "outmatrix".
	 */
	for (i = 0; i < 4; i++)
	{
	    for (j = 0; j < 4; j++)
	    {
	        tmp_matrix[i][j]  = matrix1[i][0] * matrix2[0][j] +
	      			    matrix1[i][1] * matrix2[1][j] +
	      			    matrix1[i][2] * matrix2[2][j] +
	      			    matrix1[i][3] * matrix2[3][j];
	    }
	}

	/*
	 *  Copy the temporary matrix to the final matrix (outmatrix).  We 
	 *  could have put this directly into the final matrix but this enables
	 *  the user to use the output matrix as either matrix1 or matrix2.
	 *  ie)
	 *      matrix1 = matrix1 * matrix2
	 */
	bcopy( (char *) tmp_matrix, (char *) outmatrix, sizeof(Matrix));
}



/************************************************************
*
*  MODULE NAME:  _X3D_matrix_set_ndc
*
*      PURPOSE:  This routine sets a transform
*                matrix which will map world coordinates
*                to normalized device coordinates.
*
*        INPUT:  
*
*       OUTPUT:  matrix -- a three dimensional (4x4) Real array
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

/*
 *   Three Dimensional Transform Matrix:
 *
 *          transforms three dimensional world coordinates to 
 *          normalized device coordinates.
 *
 *          ----            ---
 *          | Sx   0    0   0 |
 *          |  0   Sy   0   0 |
 *          |  0   0    0   0 |
 *          | Ax   Ay   0   1 |
 *          ----            ---
 */


_X3D_matrix_set_ndc (xv_min, xv_max, yv_min, yv_max, wc_min, wc_max, matrix)

Real	xv_min, xv_max,
	yv_min, yv_max;
Coord  wc_min, wc_max;
Matrix  matrix;
{
	Real	scale_x, scale_y, scale_z,
		offset_x, offset_y, offset_z;

	scale_x = 1/(wc_max.x - wc_min.x);
	scale_y = 1/(wc_max.y - wc_min.y);
	if(wc_max.z == wc_min.z)
	    scale_z = 0.0;
	else
	   scale_z = 1/(wc_max.z - wc_min.z);

	offset_x = 0 - (wc_min.x * scale_x);
	offset_y = 0 - (wc_min.y * scale_y);
	offset_z = 0 - (wc_min.z * scale_z);

	/*
	 *  Now set the 3D ndc matrix  
	 */
	_X3D_matrix_set_identity(matrix);

	matrix[0][0] = scale_x;
	matrix[1][1] = scale_y;
	matrix[2][2] = scale_z;

	matrix[3][0] = offset_x;
	matrix[3][1] = offset_y;
	matrix[3][2] = offset_z;
}





/************************************************************
*
*  MODULE NAME:  _X3D_write_matrix
*
*      PURPOSE:  Prints out a Matrix.  This is mostly used by
*		 for debugging purposes.
*
*
*        INPUT:  1)  matrix --  The Matrix to be printed.
*
*
*       OUTPUT:  Prints out a Matrix.
*
*    CALLED BY:
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/




_X3D_write_matrix (matrix)

Matrix matrix;
{
     int i,j;

     printf ("\n");
     for (i = 0; i < 4; i++)
     {
         for (j = 0; j < 4; j++)
	 {
	     printf (" %7.3f ",matrix[i][j]);
	 }
	 printf ("\n");
     }
}



/************************************************************
*
*  MODULE NAME:  _X3D_matrix_set_dc
*
*      PURPOSE:  This routine sets a transform matrix which will map 
*		 world coordinates to device coordinates.
*
*        INPUT:  graphics --	X3D graphics structure
*
*       OUTPUT:  matrix -- a three dimensional (4x4) Real array
*
*    CALLED BY:  _X3D_update_graphics()
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

_X3D_matrix_set_dc (graphics, matrix)

X3DGraphics	*graphics;
Matrix  	matrix;
{
	/*
	 *  Now set the 3D dc matrix  
	 */
	_X3D_matrix_set_identity(matrix);

	if (graphics->device == X11)
	{
	   matrix[0][0] = graphics->X11_xmax - graphics->X11_xmin;
	   matrix[1][1] = graphics->X11_ymin - graphics->X11_ymax;
	   matrix[3][0] = graphics->X11_xmin;
	   matrix[3][1] = graphics->X11_ymax;
	}
	else if (graphics->device == POSTSCR)
	{
	   matrix[0][0] = graphics->POS_xmax - graphics->POS_xmin;
	   matrix[1][1] = graphics->POS_ymax - graphics->POS_ymin;
	   matrix[3][0] = graphics->POS_xmin;
	   matrix[3][1] = graphics->POS_ymin;
	}
	else if (graphics->device == IMPRESS)
	{
	   matrix[0][0] = graphics->IMP_xmax - graphics->IMP_xmin;
	   matrix[1][1] = graphics->IMP_ymin - graphics->IMP_ymax;
	   matrix[3][0] = graphics->IMP_xmin;
	   matrix[3][1] = graphics->IMP_ymax;
	}
	else if (graphics->device == HPGL)
	{
	   matrix[0][0] = graphics->HPGL_xmax - graphics->HPGL_xmin;
	   matrix[1][1] = graphics->HPGL_ymax - graphics->HPGL_ymin;
	   matrix[3][0] = graphics->HPGL_xmin;
	   matrix[3][1] = graphics->HPGL_ymin;
	}
}



/************************************************************
*
*  MODULE NAME:  X3D_matrix_set_translate
*
*      PURPOSE:  This routine is used to set a translation
*                matrix in order to translate world coordinates
*                to new world coordinates.
*
*        INPUT:  xtrans --  a Real containing the x distance to be translated
*                ytrans --  a Real containing the y distance to be translated
*                ztrans --  a Real containing the z distance to be translated
*		 matrix --  an empty three dimension (4x4) Real array
*
*       OUTPUT:  matrix -- a three dimensional (4x4) Real array
*                          containing the scaling matrix.
*
*    CALLED BY:
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

/*
 *   Three Dimensional Translation Matrix:
 *
 *          transforms three dimensional world coordinates to
 *          normalized device coordinates.
 *
 *          ----            ---
 *          | 1    0    0   0 |
 *          | 0    1    0   0 |
 *          | 0    0    1   0 |
 *          | Tx   Ty   Tz  1 |
 *          ----            ---
 */

X3D_matrix_set_translate (xtrans, ytrans, ztrans, matrix)

Real	xtrans, ytrans, ztrans;
Matrix	matrix;
{
        /*
         *  Set up the translation matrix --
         *  these entries will not change
         */
	_X3D_matrix_set_identity(matrix);

        /*
         *  Values in the active 4th column of the
         *  translation matrix have been provided by the user.
         */
        matrix[3][0] = xtrans; matrix[3][1] = ytrans; matrix[3][2] = ztrans;
}



/************************************************************
*
*  MODULE NAME:  X3D_matrix_set_scale
*
*      PURPOSE:  This routine is used to set a scale
*                matrix in order to scale world coordinates
*                to new world coordinates.
*
*
*        INPUT:  xscale --  a Real containing the x distance to be translated
*                yscale --  a Real containing the y distance to be translated
*                zscale --  a Real containing the z distance to be translated
*                matrix --  an empty three dimension (4x4) Real array
*
*
*       OUTPUT:  matrix -- a three dimensional (4x4) float array
*                          containing the scaling matrix.
*
*    CALLED BY:
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

/*
 *   Three Dimensional Scaling Matrix
 *
 *          scales three dimensional world coordinates
 *
 *          ----            ---
 *          | Sx   0    0   0 |
 *          |  0   Sy   0   0 |
 *          |  0   0    Sz  0 |
 *          |  0   0    0   1 |
 *          ----            ---
 */

X3D_matrix_set_scale (xscale, yscale, zscale, matrix)

Real	xscale, yscale, zscale;
Matrix	matrix;
{


        /*
         *  set identity matrix
         */
	_X3D_matrix_set_identity(matrix);

        /*
         *  the active entries are on the diagonal, providing the
         *  scaling factor requested by the user
         */
        matrix[0][0] = xscale; matrix[1][1] = yscale; matrix[2][2] = zscale;

}


/************************************************************
*
*  MODULE NAME:  X3D_matrix_set_rotate
*
*      PURPOSE:  This routine is used to set a rotation
*                matrix in order to rotate world coordinates
*                to new world coordinates.
*
*        INPUT:  matrix --  an empty three dimension (4x4) Real array
*
*                rotation_degree --  a Real containing the degree of
*                                    rotation
*                axis   --  a character representing which axis is to
*                           be rotated
*
*
*       OUTPUT:  matrix -- a three dimensional (4x4) Real array
*                          containing the rotation matrix.
*
*    CALLED BY:
*
*   WRITTEN BY:  Mark Young
*
*
*************************************************************/

/*
 *   Three Dimensional Rotation Matrix:
 *
 *          rotates three dimensional world coordinates to
 *          new world  coordinates.
 *
 *          ----            ---
 *          | Sx   0    0   0 |
 *          |  0   Sy   0   0 |
 *          |  0   0    0   0 |
 *          | Ax   Ay   0   1 |
 *          ----            ---
 */

X3D_matrix_set_rotate (xrotate, yrotate, zrotate, matrix)

Real	xrotate, yrotate, zrotate;
Matrix	matrix;
{
        Real  rad;
	Matrix  xmatrix, ymatrix, zmatrix;


        /*
         *  initialize the matrix to the identity matrix, for
         *  the sake of simplicity -- some values will be over-
         *  written.
         */
	_X3D_matrix_set_identity(xmatrix);
	_X3D_matrix_set_identity(ymatrix);
	_X3D_matrix_set_identity(zmatrix);

        /*
         *  set up the rotation matrix as described above --
         *  with cos@ and sin@ in the proper places depending
         *  on which axis we are rotating about
         */

	/* x rotation matrix */
        rad = _X3D_DegToRads(xrotate);
        xmatrix[1][1] = xmatrix[2][2] = cos(rad);
        xmatrix[1][2] = sin(rad); xmatrix[2][1] = -sin(rad);

	/* y rotation matrix */
        rad = _X3D_DegToRads(yrotate);
        ymatrix[0][0] = ymatrix[2][2] = cos(rad);
        ymatrix[2][0] = sin(rad); ymatrix[0][2] = -sin(rad);

	/* z rotation matrix */
        rad = _X3D_DegToRads(zrotate);
        zmatrix[0][0] = zmatrix[1][1] = cos(rad);
        zmatrix[0][1] = sin(rad); zmatrix[1][0] = -sin(rad);

	/*
	 *  Get the final rotation matrix in terms of the
	 *  x, y, & z rotation.
	 */
	_X3D_matrix_mult (xmatrix, ymatrix, matrix);
	_X3D_matrix_mult (matrix,  zmatrix, matrix);
}
