/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/

/******************************************************************************
 *                                                                             *
 *   view.c:  Compute viewing direction.                                       *
 *                                                                             *
 ******************************************************************************/

#include "incl.h"

#define XAXIS		1	/* Positive X-axis points rightwards  */
#define YAXIS		2	/* Positive Y-axis points upwards     */
#define ZAXIS		3	/* Positive Z-axis points into screen */

float transformation_matrix[4][4];/* current transformation matrix of floats */
float out_invvertex[2][2][2][NM]; /* Image and object space centers          */
/*   of outer voxels in output map         */
float uout_invvertex[2][2][2][NM];/* Image and object space vertices         */
/*   of output map unit voxel              */

long frust_len;		/* Size of clipping frustum                  */
/*   (mins will be 0 in this program,        */
/*    {x,y}len will be <= IK{X,Y}SIZE)       */
float out_diag_len[NM];   	/* Per-axis lengths and combined length of   */
float out_diag_length;	        /*   diagonal of input map in image space    */
float depth_cueing[MAX_OUTLEN];	/* Pre-computed table of depth cueing        */
long image_zlen;			/*   number of samples along viewing ray     */
float in_max[NM];               /* Pre-computed clipping aids                */
long opc_xlen,opc_xylen;
long norm_xlen,norm_xylen;

float invmatrix[4][4];		/* Inverse of viewing matrix:                */
/* 4 x 4 viewing transformation matrix       */
/*   (orthographic projection used, so       */
/*    matrix[][3] = 0, matrix[3][3] = 1)     */

EXTERN_ENV

void Compute_Pre_View()
{
    long i, outz;

    for (i=0; i<NM; i++)
        out_diag_len[i] = opc_len[i]-1;
    out_diag_length = out_diag_len[X]*out_diag_len[X] +
        out_diag_len[Y]*out_diag_len[Y] +
            out_diag_len[Z]*out_diag_len[Z];
    out_diag_length = sqrt(out_diag_length);
    frust_len = NINT(out_diag_length)+1;
    image_zlen = frust_len - 1;

    /* Pre-compute table of depth cueing attenuation fractions as        */
    /* exponential falloff from intensity of depth_hither*full at        */
    /* hither (outz=0) to depth_yon*full at yon (frust_len-1).           */
    /* Exponent > 1.0 falls off slower than linear, < 1.0 falls faster.  */
    /* Clip fractions to valid range to allow agressive user             */
    /* to set hither or yon outside the range 0.0..1.0.                  */
    if (image_zlen > MAX_OUTLEN)
        Error ("MAX_OUTLEN exceeded in Ray_Trace.\n");
    for (outz=0; outz<=image_zlen; outz++) {
        depth_cueing[outz] = depth_hither -
            pow((float)(outz)/(float)image_zlen,depth_exponent) *
                (depth_hither - depth_yon);
        depth_cueing[outz] = MIN(MAX(depth_cueing[outz],0.0),1.0);
    }

    /* Pre-compute clipping aids                                         */
    in_max[X] = (float)(opc_len[X]-1)-SMALL-1.0/(float)MAX_PIXEL;
    in_max[Y] = (float)(opc_len[Y]-1)-SMALL-1.0/(float)MAX_PIXEL;
    in_max[Z] = (float)(opc_len[Z]-1)-SMALL-1.0/(float)MAX_PIXEL;

    /* Pre-compute subscripting aids                                     */
    opc_xlen = opc_len[X] + 1;
    opc_xylen = opc_len[X] * opc_len[Y] + 1;

    norm_xlen = norm_len[X] + 1;
    norm_xylen = norm_len[X] * norm_len[Y] + 1;
}


void Select_View(delta_angle, axis)
  float delta_angle;
  long axis;

{
    Load_Identity_Matrix(invmatrix);

    /* Moves input map from all-positive octant to center of     */
    /* coordinate system so subsequent rotations spin object     */
    /* in place.  Must be first after initialization to work.    */
    Inverse_Concatenate_Translation(invmatrix,
                                    (float)(out_diag_len[X])/2.0,
                                    (float)(out_diag_len[Y])/2.0,
                                    (float)(out_diag_len[Z])/2.0);

    /* scale dataset size in Z-direction for 256x256x113 dataset */
    Inverse_Concatenate_Scaling(invmatrix,1.0,1.0,1.0/(float)ZSCALE);

    /* rotation about axis by angle */
    if (frame != 0)
        angle[axis] = angle[axis] + delta_angle;
    Inverse_Concatenate_Rotation(invmatrix,XAXIS,-angle[X]);
    Inverse_Concatenate_Rotation(invmatrix,YAXIS,-angle[Y]);
    /*  Inverse_Concatenate_Rotation(invmatrix,ZAXIS,-angle[Z]);*/
    Inverse_Concatenate_Rotation(invmatrix,XAXIS,30.0);

    /* Moves input map from center of coordinate system back     */
    /* to within all-positive octant such that any rotation      */
    /* (e.g. by using pre-matrix, rotations, and anpost-matrix)  */
    /* exactly falls in the octant, preventing low-side clipping.*/
    /* Fails if non-isotropic image space scaling is applied     */
    /* (i.e. different scaling in X,Y, or Z after rotations).    */
    Inverse_Concatenate_Translation(invmatrix,
                                    -out_diag_length/2.0,
                                    -out_diag_length/2.0,
                                    -out_diag_length/2.0);

    /* Insures that frustum entirely encloses any rotation of    */
    /* map assuming they fall within the all-positive octant     */
    /* (e.g. by using pre-matrix, rotations, and anpost-matrix), */
    Load_Transformation_Matrix(invmatrix);
    Compute_Input_Dimensions();
    Compute_Input_Unit_Vector();
}


void Compute_Input_Dimensions()
{
    long x,y,z;
    float in_invvertex[2][2][2][NM];	/* Image and object space centers    */

    in_invvertex[0][0][0][X] = 0;
    in_invvertex[0][0][0][Y] = 0;
    in_invvertex[0][0][0][Z] = 0;

    in_invvertex[0][0][1][X] = frust_len-1;
    in_invvertex[0][0][1][Y] = 0;
    in_invvertex[0][0][1][Z] = 0;

    in_invvertex[0][1][0][X] = 0;
    in_invvertex[0][1][0][Y] = frust_len-1;
    in_invvertex[0][1][0][Z] = 0;

    in_invvertex[0][1][1][X] = frust_len-1;
    in_invvertex[0][1][1][Y] = frust_len-1;
    in_invvertex[0][1][1][Z] = 0;

    in_invvertex[1][0][0][X] = 0;
    in_invvertex[1][0][0][Y] = 0;
    in_invvertex[1][0][0][Z] = frust_len-1;

    in_invvertex[1][0][1][X] = frust_len-1;
    in_invvertex[1][0][1][Y] = 0;
    in_invvertex[1][0][1][Z] = frust_len-1;

    in_invvertex[1][1][0][X] = 0;
    in_invvertex[1][1][0][Y] = frust_len-1;
    in_invvertex[1][1][0][Z] = frust_len-1;

    in_invvertex[1][1][1][X] = frust_len-1;
    in_invvertex[1][1][1][Y] = frust_len-1;
    in_invvertex[1][1][1][Z] = frust_len-1;

    for (z=0; z<2; z++) {
        for (y=0; y<2; y++) {
            for (x=0; x<2; x++) {
                Transform_Point(in_invvertex[z][y][x][X],
                                in_invvertex[z][y][x][Y],
                                in_invvertex[z][y][x][Z],
                                &out_invvertex[z][y][x][X],
                                &out_invvertex[z][y][x][Y],
                                &out_invvertex[z][y][x][Z]);
            }
        }
    }
}


void Compute_Input_Unit_Vector()
{
    long x,y,z;
    float uin_invvertex[2][2][2][NM];

    uin_invvertex[0][0][0][X] = 0;
    uin_invvertex[0][0][0][Y] = 0;
    uin_invvertex[0][0][0][Z] = 0;

    uin_invvertex[0][0][1][X] = 1.0;
    uin_invvertex[0][0][1][Y] = 0;
    uin_invvertex[0][0][1][Z] = 0;

    uin_invvertex[0][1][0][X] = 0;
    uin_invvertex[0][1][0][Y] = 1.0;
    uin_invvertex[0][1][0][Z] = 0;

    uin_invvertex[0][1][1][X] = 1.0;
    uin_invvertex[0][1][1][Y] = 1.0;
    uin_invvertex[0][1][1][Z] = 0;

    uin_invvertex[1][0][0][X] = 0;
    uin_invvertex[1][0][0][Y] = 0;
    uin_invvertex[1][0][0][Z] = 1.0;

    uin_invvertex[1][0][1][X] = 1.0;
    uin_invvertex[1][0][1][Y] = 0;
    uin_invvertex[1][0][1][Z] = 1.0;

    uin_invvertex[1][1][0][X] = 0;
    uin_invvertex[1][1][0][Y] = 1.0;
    uin_invvertex[1][1][0][Z] = 1.0;

    uin_invvertex[1][1][1][X] = 1.0;
    uin_invvertex[1][1][1][Y] = 1.0;
    uin_invvertex[1][1][1][Z] = 1.0;

    for (z=0; z<2; z++) {
        for (y=0; y<2; y++) {
            for (x=0; x<2; x++) {
                Transform_Point(uin_invvertex[z][y][x][X],
                                uin_invvertex[z][y][x][Y],
                                uin_invvertex[z][y][x][Z],
                                &uout_invvertex[z][y][x][X],
                                &uout_invvertex[z][y][x][Y],
                                &uout_invvertex[z][y][x][Z]);
            }
        }
    }
}


void Load_Transformation_Matrix(matrix)
  float matrix[4][4];
{
	Copy_Matrix(matrix,transformation_matrix);
}


void Transform_Point(xold,yold,zold,xnew,ynew,znew)
  float xold,yold,zold;
  float *xnew,*ynew,*znew;
{
	*xnew =
	    xold * transformation_matrix[0][0] +
            yold * transformation_matrix[1][0] +
                zold * transformation_matrix[2][0] +
                    transformation_matrix[3][0];

	*ynew =
	    xold * transformation_matrix[0][1] +
            yold * transformation_matrix[1][1] +
                zold * transformation_matrix[2][1] +
                    transformation_matrix[3][1];

	*znew =
	    xold * transformation_matrix[0][2] +
            yold * transformation_matrix[1][2] +
                zold * transformation_matrix[2][2] +
                    transformation_matrix[3][2];
}


void Inverse_Concatenate_Translation(matrix,xoffset,yoffset,zoffset)
  float matrix[4][4],xoffset,yoffset,zoffset;
{
	float translation_matrix[4][4];
	Load_Translation_Matrix(translation_matrix,xoffset,yoffset,zoffset);
	Inverse_Concatenate_Transform(matrix,translation_matrix);
}


void Inverse_Concatenate_Scaling(matrix,xscale,yscale,zscale)
  float matrix[4][4],xscale,yscale,zscale;
{
	float scaling_matrix[4][4];
	Load_Scaling_Matrix(scaling_matrix,xscale,yscale,zscale);
	Inverse_Concatenate_Transform(matrix,scaling_matrix);
}


void Inverse_Concatenate_Rotation(matrix,axis,angle)
  float matrix[4][4],angle;
  long axis;
{
	float rotation_matrix[4][4];
	Load_Rotation_Matrix(rotation_matrix,axis,angle);
	Inverse_Concatenate_Transform(matrix,rotation_matrix);
}


void Load_Identity_Matrix(matrix)
  float matrix[4][4];
{
	long i,j;
	for (i=0; i<4; i++) {
		for (j=0; j<4; j++) {
			matrix[i][j] = 0;
		}
		matrix[i][i] = 1.0;
	}
}

void Load_Translation_Matrix(matrix,xoffset,yoffset,zoffset)
  float matrix[4][4],xoffset,yoffset,zoffset;
{
	Load_Identity_Matrix(matrix);
	matrix[3][0] = xoffset;
	matrix[3][1] = yoffset;
	matrix[3][2] = zoffset;
}


void Load_Scaling_Matrix(matrix,xscale,yscale,zscale)
  float matrix[4][4],xscale,yscale,zscale;
{
	Load_Identity_Matrix(matrix);
	matrix[0][0] = xscale;
	matrix[1][1] = yscale;
	matrix[2][2] = zscale;
}


void Load_Rotation_Matrix(matrix,axis,angle)
  float matrix[4][4],angle;
  long axis;
{
	Load_Identity_Matrix(matrix);
	if (axis == XAXIS) {
		matrix[1][1] = cos(angle/180.0*PI);
		matrix[1][2] = sin(angle/180.0*PI);
		matrix[2][1] = -sin(angle/180.0*PI);
		matrix[2][2] = cos(angle/180.0*PI);
	}
	else if (axis == YAXIS) {
		matrix[0][0] = cos(angle/180.0*PI);
		matrix[0][2] = -sin(angle/180.0*PI);
		matrix[2][0] = sin(angle/180.0*PI);
		matrix[2][2] = cos(angle/180.0*PI);
	}
	else if (axis == ZAXIS) {
		matrix[0][0] = cos(angle/180.0*PI);
		matrix[0][1] = sin(angle/180.0*PI);
		matrix[1][0] = -sin(angle/180.0*PI);
		matrix[1][1] = cos(angle/180.0*PI);
	}
}


void Concatenate_Transform(composite_matrix,transformation_matrix)
  float composite_matrix[][4],transformation_matrix[][4];
{
	float temp_matrix[4][4];
	Multiply_Matrices(composite_matrix,transformation_matrix,temp_matrix);
	Copy_Matrix(temp_matrix,composite_matrix);
}

void Inverse_Concatenate_Transform(composite_matrix,transformation_matrix)
  float composite_matrix[][4],transformation_matrix[][4];
{
	float temp_matrix[4][4];
	Multiply_Matrices(transformation_matrix,composite_matrix,temp_matrix);
	Copy_Matrix(temp_matrix,composite_matrix);
}


void Multiply_Matrices(input_matrix1,input_matrix2,output_matrix)
  float input_matrix1[][4],input_matrix2[][4],output_matrix[][4];
{
	long i,j;
	for (i=0; i<4; i++) {
		for (j=0; j<4; j++) {
			output_matrix[i][j] =
			    input_matrix1[i][0] *
			    input_matrix2[0][j] +
			    input_matrix1[i][1] *
			    input_matrix2[1][j] +
			    input_matrix1[i][2] *
			    input_matrix2[2][j] +
			    input_matrix1[i][3] *
			    input_matrix2[3][j];
		}
	}
}


void Copy_Matrix(input_matrix,output_matrix)
float input_matrix[][4],output_matrix[][4];
{
	long i,j;
	for (i=0; i<4; i++) {
		for (j=0; j<4; j++) {
			output_matrix[i][j] = input_matrix[i][j];
		}
	}
}
