/* transform.c,v 1.1.1.1 1995/02/27 07:38:28 explorer Exp */

/*
 * Copyright (C) 1989, 1991, Craig E. Kolb
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

#include "common.h"
#include "rotate.h"
#include "translate.h"
#include "scale.h"

extern int yylineno;

/*
 * Matrices are indexed row-first; that is:
 * matrix[ROW][COLUMN]
 */

/*
 * Allocate new structure that holds both object-to-world and
 * world-to-object space transformation structures.  It probably
 * should hold pointers to these structures.
 */
Trans *
TransCreate(tr, meth)
     TransRef tr;
     TransMethods *meth;
{
  Trans *res;
  
  res = (Trans *)share_malloc(sizeof(Trans));
  res->tr = tr;
  res->methods = meth;
  res->animated = FALSE;
  res->assoc = (ExprAssoc *)NULL;
  res->prev = res->next = (Trans *)NULL;
  MatrixInit(&res->trans);
  MatrixInit(&res->itrans);
  return res;
}

void
TransFree(trans)
     Trans *trans;
{
  if (trans->tr)
    free((voidstar)trans->tr);
  free((voidstar)trans);
}

void
TransAssoc(trans, ptr, expr)
     Trans *trans;
     Float *ptr;
     Expr *expr;
{
  if (expr->timevary) {
    /*
     * Gotta store the sucker.
     */
    trans->assoc = AssocCreate(ptr, expr, trans->assoc);
    trans->animated = TRUE;
  } else {
    *ptr = expr->value;
  }
  fflush(stderr);
}

/*
 * Allocate new transformation 'matrix'.
 */
RSMatrix *
MatrixCreate()
{
  RSMatrix *res;
  
  res = (RSMatrix *)share_malloc(sizeof(RSMatrix));
  MatrixInit(res);
  return res;
}

/*
 * Multiply m1 and m2, copy result into "res".
 */
void
MatrixMult(t1, t2, res)
     RSMatrix *t1, *t2, *res;
{
  register int i;
  RSMatrix tmp;
  
  for (i = 0; i < 3; i++) {
    tmp.matrix[i][0] = (t1->matrix[i][0] * t2->matrix[0][0] +
			t1->matrix[i][1] * t2->matrix[1][0] +
			t1->matrix[i][2] * t2->matrix[2][0]);
    tmp.matrix[i][1] = (t1->matrix[i][0] * t2->matrix[0][1] +
			t1->matrix[i][1] * t2->matrix[1][1] +
			t1->matrix[i][2] * t2->matrix[2][1]);
    tmp.matrix[i][2] = (t1->matrix[i][0] * t2->matrix[0][2] +
			t1->matrix[i][1] * t2->matrix[1][2] +
			t1->matrix[i][2] * t2->matrix[2][2]);
  }
  
  tmp.translate.x = (t1->translate.x * t2->matrix[0][0] +
		     t1->translate.y * t2->matrix[1][0] +
		     t1->translate.z * t2->matrix[2][0] + t2->translate.x);
  tmp.translate.y = (t1->translate.x * t2->matrix[0][1] +
		     t1->translate.y * t2->matrix[1][1] +
		     t1->translate.z * t2->matrix[2][1] + t2->translate.y);
  tmp.translate.z = (t1->translate.x * t2->matrix[0][2] +
		     t1->translate.y * t2->matrix[1][2] +
		     t1->translate.z * t2->matrix[2][2] + t2->translate.z);
  MatrixCopy(&tmp, res);
}

/*
 * Return transformation information to map the "coordinate system"
 * with the given origin, "up" vector, radius, and up axis lengths to
 * one in which the "up" vector is the Z axis and the x/y/up axes
 * have unit length.  This is useful for transforming a general
 * form of a primitive into a canonical, Z-axis aligned, unit size
 * primitive, facilitating intersection testing.
 * Assumes that "up" is normalized.
 */
void
CoordSysTransform(origin, up, r, len, trans)
     Vector *origin, *up;
     Float r, len;
     Trans *trans;
{
  RSMatrix tmp;
  Vector atmp;
  
  ScaleMatrix(r, r, len, &trans->trans);
  if (1. - fabs(up->z) < EPSILON) {
    atmp.x = 1.;
    atmp.y = atmp.z = 0.;
  } else {
    atmp.x = up->y;
    atmp.y = -up->x;
    atmp.z= 0.;
  }
  /*
   * Might want to make sure that |up->z| is < 1.
   */
  RotationMatrix(atmp.x, atmp.y, atmp.z, -acos(up->z), &tmp);
  MatrixMult(&trans->trans, &tmp, &trans->trans);
  TranslationMatrix(origin->x, origin->y, origin->z, &tmp);
  MatrixMult(&trans->trans, &tmp, &trans->trans);
  MatrixInvert(&trans->trans, &trans->itrans);
}

void
TransCopy(from, into)
     Trans *into, *from;
{
  MatrixCopy(&from->trans, &into->trans);
  MatrixCopy(&from->itrans, &into->itrans);
}

void
TransInvert(from, into)
     Trans *into, *from;
{
  RSMatrix ttmp;
  /*
   * In case into == from...
   */
  if (from == into) {
    ttmp = from->trans;
    into->trans = from->itrans;
    into->itrans = ttmp;
  } else {
    into->trans = from->itrans;
    into->itrans = from->trans;
  }
}

/*
 * Copy a given transformation structure.
 */
void
MatrixCopy(from, into)
     RSMatrix *into, *from;
{
  into->matrix[0][0] = from->matrix[0][0];
  into->matrix[0][1] = from->matrix[0][1];
  into->matrix[0][2] = from->matrix[0][2];
  into->matrix[1][0] = from->matrix[1][0];
  into->matrix[1][1] = from->matrix[1][1];
  into->matrix[1][2] = from->matrix[1][2];
  into->matrix[2][0] = from->matrix[2][0];
  into->matrix[2][1] = from->matrix[2][1];
  into->matrix[2][2] = from->matrix[2][2];
  into->translate = from->translate;
}

void
TransInit(trans)
     Trans *trans;
{
  MatrixInit(&trans->trans);
  MatrixInit(&trans->itrans);
}

void
TransCompose(t1, t2, res)
     Trans *t1, *t2, *res;
{
  MatrixMult(&t1->trans, &t2->trans, &res->trans);
  MatrixMult(&t2->itrans, &t1->itrans, &res->itrans);
}

/*
 * Initialize transformation structure.
 */
void
MatrixInit(trans)
     RSMatrix *trans;
{
  trans->matrix[0][0] = trans->matrix[1][1] = trans->matrix[2][2] = 1.;
  trans->matrix[0][1] = trans->matrix[0][2] = trans->matrix[1][0] =
    trans->matrix[1][2] = trans->matrix[2][0] = trans->matrix[2][1] = 0.;
  trans->translate.x = trans->translate.y = trans->translate.z = 0.;
}

/*
 * Calculate inverse of the given transformation structure.
 */
void
MatrixInvert(trans, inverse)
     RSMatrix *inverse, *trans;
{
  RSMatrix ttmp;
  Float d;
  
  ttmp.matrix[0][0] = (trans->matrix[1][1]*trans->matrix[2][2] -
		       trans->matrix[1][2]*trans->matrix[2][1]);
  ttmp.matrix[1][0] = (trans->matrix[1][0]*trans->matrix[2][2] -
		       trans->matrix[1][2]*trans->matrix[2][0]);
  ttmp.matrix[2][0] = (trans->matrix[1][0]*trans->matrix[2][1] -
		       trans->matrix[1][1]*trans->matrix[2][0]);
  
  ttmp.matrix[0][1] = (trans->matrix[0][1]*trans->matrix[2][2] -
		       trans->matrix[0][2]*trans->matrix[2][1]);
  ttmp.matrix[1][1] = (trans->matrix[0][0]*trans->matrix[2][2] -
		       trans->matrix[0][2]*trans->matrix[2][0]);
  ttmp.matrix[2][1] = (trans->matrix[0][0]*trans->matrix[2][1] -
		       trans->matrix[0][1]*trans->matrix[2][0]);
  
  ttmp.matrix[0][2] = (trans->matrix[0][1]*trans->matrix[1][2] -
		       trans->matrix[0][2]*trans->matrix[1][1]);
  ttmp.matrix[1][2] = (trans->matrix[0][0]*trans->matrix[1][2] -
		       trans->matrix[0][2]*trans->matrix[1][0]);
  ttmp.matrix[2][2] = (trans->matrix[0][0]*trans->matrix[1][1] -
		       trans->matrix[0][1]*trans->matrix[1][0]);
  
  d = (trans->matrix[0][0]*ttmp.matrix[0][0] -
       trans->matrix[0][1]*ttmp.matrix[1][0] +
       trans->matrix[0][2]*ttmp.matrix[2][0]);
  
  if (fabs(d) < EPSILON*EPSILON)
    RLerror(RL_PANIC, "Singular matrix.\n",yylineno);
  
  ttmp.matrix[0][0] /= d;
  ttmp.matrix[0][2] /= d;
  ttmp.matrix[1][1] /= d;
  ttmp.matrix[2][0] /= d;
  ttmp.matrix[2][2] /= d;
  
  d = -d;
  
  ttmp.matrix[0][1] /= d;
  ttmp.matrix[1][0] /= d;
  ttmp.matrix[1][2] /= d;
  ttmp.matrix[2][1] /= d;
  
  ttmp.translate.x = -(ttmp.matrix[0][0]*trans->translate.x +
		       ttmp.matrix[1][0]*trans->translate.y +
		       ttmp.matrix[2][0]*trans->translate.z);
  ttmp.translate.y = -(ttmp.matrix[0][1]*trans->translate.x +
		       ttmp.matrix[1][1]*trans->translate.y +
		       ttmp.matrix[2][1]*trans->translate.z);
  ttmp.translate.z = -(ttmp.matrix[0][2]*trans->translate.x +
		       ttmp.matrix[1][2]*trans->translate.y +
		       ttmp.matrix[2][2]*trans->translate.z);
  
  MatrixCopy(&ttmp, inverse);
}

/* find eigenvalues of positive definite matrices, return FALSE is singular. */
int
MatrixEigenvalues(trans, lambda)
     RSMatrix *trans;
     Float lambda[3];
{
  Float a[3], maxval, a11, a12, a21, a22;
  Float *A[3];
  
  a[0] = fabs(trans->matrix[0][0]); maxval = a[0];
  a[1] = fabs(trans->matrix[1][0]); if (a[1] > maxval) maxval = a[1];
  a[2] = fabs(trans->matrix[2][0]); if (a[2] > maxval) maxval = a[2];
  
  if (maxval == a[0]) {
    A[0] = trans->matrix[0];
    A[1] = trans->matrix[1];
    A[2] = trans->matrix[2];
  } else if (maxval == a[1]) {
    A[0] = trans->matrix[1];
    A[1] = trans->matrix[0];
    A[2] = trans->matrix[2];
  } else {
    A[0] = trans->matrix[2];
    A[1] = trans->matrix[1];
    A[2] = trans->matrix[0];
  }
  
  lambda[0] = A[0][0];
  if (lambda[0] == 0.) return FALSE;
  
  a[1] = A[1][0] / lambda[0];
  a[2] = A[2][0] / lambda[0];
  a11 = A[1][1] - a[1] * A[0][1];        
  a21 = A[2][1] - a[2] * A[0][1];        
  a12 = A[1][2] - a[1] * A[0][2];        
  a22 = A[2][2] - a[2] * A[0][2];        
  
  if (fabs(a11) > fabs(a21)) {
    lambda[1] = a11;
    if (lambda[1] == 0.) return FALSE;
    lambda[2] = a22 - a21/a11 * a12;
  } else {
    lambda[1] = a21;
    if (lambda[1] == 0.) return FALSE;
    lambda[2] = a12 - a11/a21 * a22;
  }
  
  if (lambda[2] == 0.) return FALSE;
  return TRUE;
}

/* Lipschitz number for euclidian norm: largest scaling factor */
Float Lipschitz(lambda)
     Float lambda[3];
{
  Float maxlambda;
  
  maxlambda = lambda[0];
  if (lambda[1] > maxlambda) maxlambda = lambda[1];
  if (lambda[2] > maxlambda) maxlambda = lambda[2];
  return maxlambda;
}

/* returns TRUE if the three scaling factors are equal */
int Uniform(lambda)
     Float lambda[3];
{
  return (equal(lambda[0], lambda[1]) && equal(lambda[1], lambda[2]));
}

/* scaling factors */
int MatrixScaling(trans, lambda)
     RSMatrix *trans;
     Float lambda[3];
{
  RSMatrix Q2;
  
  /* Q-R decomposition: we are only interested in Q**2 */
  Q2.matrix[0][0] = (trans->matrix[0][0] * trans->matrix[0][0] +
		     trans->matrix[0][1] * trans->matrix[0][1] +
		     trans->matrix[0][2] * trans->matrix[0][2]);
  Q2.matrix[0][1] = (trans->matrix[0][0] * trans->matrix[1][0] +
		     trans->matrix[0][1] * trans->matrix[1][1] +
		     trans->matrix[0][2] * trans->matrix[1][2]);
  Q2.matrix[0][2] = (trans->matrix[0][0] * trans->matrix[2][0] +
		     trans->matrix[0][1] * trans->matrix[2][1] +
		     trans->matrix[0][2] * trans->matrix[2][2]);
  Q2.matrix[1][1] = (trans->matrix[1][0] * trans->matrix[1][0] +
		     trans->matrix[1][1] * trans->matrix[1][1] +
		     trans->matrix[1][2] * trans->matrix[1][2]);
  Q2.matrix[1][2] = (trans->matrix[1][0] * trans->matrix[2][0] +
		     trans->matrix[1][1] * trans->matrix[2][1] +
		     trans->matrix[1][2] * trans->matrix[2][2]);
  Q2.matrix[2][2] = (trans->matrix[2][0] * trans->matrix[2][0] +
		     trans->matrix[2][1] * trans->matrix[2][1] +
		     trans->matrix[2][2] * trans->matrix[2][2]);
  Q2.matrix[1][0] = Q2.matrix[0][1];
  Q2.matrix[2][0] = Q2.matrix[0][2];
  Q2.matrix[2][1] = Q2.matrix[1][2];
  
  /* scaling factors are square root of eigenvalues of Q**2 */
  if (!MatrixEigenvalues(&Q2, lambda)) return FALSE;
  lambda[0] = sqrt(lambda[0]);
  lambda[1] = sqrt(lambda[1]);
  lambda[2] = sqrt(lambda[2]);
  return TRUE;
}

/*
 * Apply a transformation to a point (translation affects the point).
 */
void
PointTransform(vec, trans)
     Vector *vec;
     RSMatrix *trans;
{
  Vector tmp;
  
  tmp.x = (vec->x * trans->matrix[0][0] + vec->y * trans->matrix[1][0] +
	   vec->z * trans->matrix[2][0] + trans->translate.x);
  tmp.y = (vec->x * trans->matrix[0][1] + vec->y * trans->matrix[1][1] +
	   vec->z * trans->matrix[2][1] + trans->translate.y);
  tmp.z = (vec->x * trans->matrix[0][2] + vec->y * trans->matrix[1][2] +
	   vec->z * trans->matrix[2][2] + trans->translate.z);
  *vec = tmp;
}

/*
 * 'c1x' is the X (0th) component of the first column, and so on.
 */
void
ArbitraryMatrix(c1x, c2x, c3x, c1y, c2y, c3y, c1z, c2z, c3z, tx, ty, tz, trans)
     Float c1x, c1y, c1z, c2x, c2y, c2z, c3x, c3y, c3z, tx, ty, tz;
     RSMatrix *trans;
{
  trans->matrix[0][0] = c1x;
  trans->matrix[1][0] = c1y;
  trans->matrix[2][0] = c1z;
  
  trans->matrix[0][1] = c2x;
  trans->matrix[1][1] = c2y;
  trans->matrix[2][1] = c2z;
  
  trans->matrix[0][2] = c3x;
  trans->matrix[1][2] = c3y;
  trans->matrix[2][2] = c3z;
  
  trans->translate.x = tx;
  trans->translate.y = ty;
  trans->translate.z = tz;
}

/*
 * Apply transformation to a vector (translations have no effect).
 */
void
VecTransform(vec, trans)
     Vector *vec;
     RSMatrix *trans;
{
  Vector tmp;
  
  tmp.x = (vec->x*trans->matrix[0][0] +
	   vec->y*trans->matrix[1][0] + vec->z*trans->matrix[2][0]);
  tmp.y = (vec->x*trans->matrix[0][1] +
	   vec->y*trans->matrix[1][1] + vec->z*trans->matrix[2][1]);
  tmp.z = (vec->x*trans->matrix[0][2] +
	   vec->y*trans->matrix[1][2] + vec->z*trans->matrix[2][2]);
  
  *vec = tmp;
}

/*
 * Transform normal -- multiply by the transpose of the given
 * matrix (which is the inverse of the 'desired' transformation).
 */
void
NormalTransform(norm, it)
     Vector *norm;
     RSMatrix *it;
{
  Vector onorm;
  
  onorm = *norm;
  
  norm->x = (onorm.x*it->matrix[0][0] + onorm.y*it->matrix[0][1] +
	     onorm.z*it->matrix[0][2]);
  norm->y = (onorm.x*it->matrix[1][0] + onorm.y*it->matrix[1][1] +
	     onorm.z*it->matrix[1][2]);
  norm->z = (onorm.x*it->matrix[2][0] + onorm.y*it->matrix[2][1] +
	     onorm.z*it->matrix[2][2]);
  (void)VecNormalize(norm);
}

/*
 * Transform "ray" by transforming the origin point and direction vector.
 */
Float
RayTransform(ray, trans)
     Ray *ray;
     RSMatrix *trans;
{
  Float stretch;
  
  PointTransform(&ray->pos, trans);
  VecTransform(&ray->dir, trans);
  
  stretch = VecNormalize(&ray->dir);
  ray->stretch *= stretch;
  return stretch;
}

void
TransPropagate(trans)
     Trans *trans;
{
  (*trans->methods->propagate)(trans->tr, &trans->trans, &trans->itrans);
}

void
TransResolveAssoc(trans)
     Trans *trans;
{
  Trans *curtrans;
  ExprAssoc *curassoc;
  
  for (curtrans = trans; curtrans; curtrans = curtrans->next) {
    for (curassoc = curtrans->assoc; curassoc; curassoc = curassoc->next) {
      *curassoc->lhs = ExprEval(curassoc->expr);
    }
    if (curtrans->assoc)
      TransPropagate(curtrans);
  }
}

void
TransComposeList(list, result)
     Trans *list, *result;
{
  TransCopy(list, result);
  for (list = list->next; list; list = list->next)
    /* it was: 
     * TransCompose(list, result, result);
     * PhB made it:
     */
    TransCompose(result, list, result);
}
