#ident "%W% %G%"
 
/**************************************************************************
# Copyright (C) 1994 Kubota Graphics Corp.
# 
# Permission to use, copy, modify, and distribute this material for
# any purpose and without fee is hereby granted, provided that the
# above copyright notice and this permission notice appear in all
# copies, and that the name of Kubota Graphics not be used in
# advertising or publicity pertaining to this material.  Kubota
# Graphics Corporation MAKES NO REPRESENTATIONS ABOUT THE ACCURACY
# OR SUITABILITY OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED
# "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE AND KUBOTA GRAPHICS CORPORATION DISCLAIMS ALL WARRANTIES,
# EXPRESS OR IMPLIED.
**************************************************************************/
 
/*****************************************************************************
				TRANSFORMERS

Included in this module:

	DtInt make_transformer(comptype)
	DtObject change_transformer(transnum, transtype, transvalue,
		comptype, newobj)
	void get_transformer_matrix(transnum, usermatrix)
	void put_transformer_matrix(transnum, usermatrix)
*****************************************************************************/



#include <dore.h>
#include "dore_util.h"

#define X 0
#define Y 1
#define Z 2


struct transformer {
	DtMatrix4x4 M;
	DtCompType comptype;
} *T;


static DtInt transcount=0, arraysize=0;

/* prototypes for static functions */
transform_matrix (DtMatrix4x4, DtInt, DtReal, DtCompType);
translate (DtMatrix4x4, int, DtReal, DtCompType);
scale(DtMatrix4x4, int, DtReal, DtCompType);
rotate(DtMatrix4x4, int, int, DtReal, DtCompType);



/*
Returns a reference number for a transformer or -1 if failed.
This number is used when requesting a new DoTransformationMatrix
object via change_transformer.
"comptype" SPECIFIES HOW THE NEW COMPOSITE MATRICIES WILL OPERATE
WITHIN THE GROUPS INTO WHICH THE USER PLACES THE DoTransformation-
Matrix OBJECTS RETURNED FROM change_transformer.
DcPreConcatenate means that the transformer object's transformations
appear to be applied first, while a transformer object with a
specification of DcPostConatenate appears to be applied last.
A value of DcReplace will cause Dore to replace the Current
Transformation Matrix with the transformer specified.
*/

DtInt make_transformer (
    DtCompType comptype)
{
	struct transformer *newtrans;

	if((DcPreConcatenate != comptype) &&
	   (DcPostConcatenate != comptype) &&
	   (DcReplace != comptype)) {
		printf("make_transformer: invalid comptype %d\n", comptype);
		return(-1);
	}
	if(transcount == arraysize) {
		if(0 == arraysize) /* first time */
			T = (struct transformer *)
				malloc((arraysize+100) * sizeof(*T));
		else
			T = (struct transformer *)
				realloc(T, (arraysize+100) * sizeof(*T));
		arraysize += 100;
		if(!T) {
			printf("make_transformer: out of memory\n");
			return(-1);
		}
	}
	newtrans = &T[transcount++];
	load_identity(newtrans->M);
	newtrans->comptype = comptype;
	return(transcount-1);
}









/*           ****  change_transformer  ****
Adds a transformation onto the transformer matrix specified by "transnum"
which must be a transformer ID generated by make_transformer().
"transtype" must be one of the transformation types listed in dore_util.h.
"transvalue" is the magnitude of the current transformation.
"comptype" specifies how the new change is to be applied TO THE
TRANSFORMER MATRIX.
DcPreConcatenate means that the new change appears as if it were
the first operation the transformer performs, while a change multiplied 
in with DcPostConatenate appears to be applied last.
DcReplace replaces the transformer with a new transformer containing
the single transformation specified.
"newobj" is a flag which specifies whether to return a Dore transform
matrix object resulting from the new change. If this flag is "DcFalse",
the new transformation is accumulated, but a transform matrix object is
not created. The value "DcNullObject" is returned instead.
*/

DtObject change_transformer (
    DtInt transnum,
    DtInt transtype,
    DtReal transvalue,
    DtCompType comptype,
    DtFlag newobj)
{
	if((0 > transnum) || (transnum> transcount)) {
		printf("change_transformer: invalid transnum%d\n",
			transnum);
		return(DcNullObject);
	}
	if((0 > transtype) || (transtype > Reset)) {
		printf("change_transformer: invalid changetype %d\n",
			transtype);
		return(DcNullObject);
	}
	if((DcPreConcatenate != comptype) &&
	   (DcPostConcatenate != comptype) &&
	   (DcReplace != comptype)) {
		printf("change_transformer: invalid comptype %d\n", comptype);
		return(DcNullObject);
	}
	transform_matrix(T[transnum].M, transtype, transvalue, comptype);
	if(newobj)
		return(DoTransformMatrix(
			T[transnum].M, T[transnum].comptype));
	else return(DcNullObject);
}






/*             **** get_transformer_matrix ****
copies the transformer matrix specified by "transnum" into the
user supplied matrix "usermatrix".
"transnum" must be a transformer ID generated by make_transformer().
*/

void get_transformer_matrix (
    DtInt transnum,
    DtMatrix4x4 usermatrix)
{
	load_matrix(usermatrix, T[transnum].M);
}





/*             **** put_transformer_matrix ****
copies the user supplied matrix "usermatrix" into the transformer matrix
specified by "transnum".
"transnum" must be a transformer ID generated by make_transformer().
*/

void put_transformer_matrix (
    DtInt transnum,
    DtMatrix4x4 usermatrix)
{
	load_matrix(T[transnum].M, usermatrix);
}






/*
adds a transformation to a transformer matrix.
*/

static transform_matrix (
    DtMatrix4x4 matrix,
    DtInt transtype,
    DtReal value,
    DtCompType comptype)
{
	switch(transtype)
		{
		case NoTransf:
			break;
		case TransX:
			translate(matrix, X, value, comptype);
			break;
		case TransY:
			translate(matrix, Y, value, comptype);
			break;
		case TransZ:
			translate(matrix, Z, value, comptype);
			break;
		case ScaleX:
			scale(matrix, X, value, comptype);
			break;
		case ScaleY:
			scale(matrix, Y, value, comptype);
			break;
		case ScaleZ:
			scale(matrix, Z, value, comptype);
			break;
		case RotX:
			rotate(matrix, Y, Z, value, comptype);
			break;
		case RotY:
			rotate(matrix, Z, X, value, comptype);
			break;
		case RotZ:
			rotate(matrix, X, Y, value, comptype);
			break;
		case Reset:
			load_identity(matrix);
			break;
		default:
			printf("transform_matrix: unknown transtype %d\n",
				transtype);
			break;
		}
}





/****************** matrix manipulation routines ******************/

static DtMatrix4x4 identity =
	{
		{1., 0., 0., 0.},
	 	{0., 1., 0., 0.},
	 	{0., 0., 1., 0.},
		{0., 0., 0., 1.}
	};



static load_identity (
    DtMatrix4x4 matrix)
{
	load_matrix(matrix, identity);
}



static load_matrix (
    DtMatrix4x4 matrix,
    DtMatrix4x4 newmat)

{
	DtInt row, col;

	for (row=0;row<4;row++)
		for (col=0;col<4;col++)
			matrix[row][col] = newmat[row][col];
}



/*  Pre-concatenate transformation represented by concatmatrix
with transformation represented by matrix. Since we are using
column vectors this is a matrix post-multiply!  */

static pre_concatenate (
    DtMatrix4x4 matrix,
    DtMatrix4x4 concatmatrix,
    DtMatrix4x4 result)

{
	DtMatrix4x4 tempmatrix;
	DtInt row, col;
	DtInt i;

	for (row=0;row<4;row++)
		for (col=0;col<4;col++) {
			tempmatrix[row][col] = 0.;
			for (i=0;i<4;i++)
				tempmatrix[row][col] +=
					matrix[row][i]*concatmatrix[i][col];
		}
	load_matrix(result, tempmatrix);
}


int concatenate (
    DtMatrix4x4 onto,
    DtMatrix4x4 from,
    DtCompType compose_type)
{
	switch(compose_type) {
		case DcPreConcatenate:
			pre_concatenate(onto, from, onto);
			break;
		case DcPostConcatenate:
			pre_concatenate(from, onto, onto);
			break;
		case DcReplace:
			load_matrix(onto, from);
			break;
	}
}



static translate (
    DtMatrix4x4 matrix,
    int axis,
    DtReal transvalue,
    DtCompType compose_type)
{
	DtMatrix4x4 tempmat;

	load_identity(tempmat);
	tempmat[axis][3] = transvalue;
	concatenate(matrix, tempmat, compose_type);
}





static scale (
    DtMatrix4x4 matrix,
    int axis,
    DtReal scalevalue,
    DtCompType compose_type)
{
	DtMatrix4x4 tempmat;

	load_identity(tempmat);
	tempmat[axis][axis] = scalevalue;
	concatenate(matrix, tempmat, compose_type);
}





static rotate (
    DtMatrix4x4 matrix,
    int u,
    int v,
    DtReal radians,
    DtCompType compose_type)
{
	double c, s, cos(), sin();

	DtMatrix4x4 tempmat;

	load_identity(tempmat);
	c = cos(radians);
	s = sin(radians);
	tempmat[u][u] = c;
	tempmat[u][v] = -s;
	tempmat[v][u] = s;
	tempmat[v][v] = c;
	concatenate(matrix, tempmat, compose_type);
}
