// Copyright (c) 1999 Philip A. Hardin (pahardin@cs.utexas.edu)
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License v2 or later.


#ifndef TRANSFORM_h
#define TRANSFORM_h


#include <iostream.h>
#include "general.h"
#include "pt3d.h"
#include "ang3d.h"


//==========================================================================
// Transformation matrix classes
// 
// tcomp - an affine transformation, represented as a cartesian offset,
// a 3D angle, and a scaling factor.
//
// tmtrx - an affine transformation represented as a 4x4 matrix


//===========================================================================
class tmtrx;  // forward reference

class tcomp {
  pt3d  cart;
  ang3d ang;
  pt3d  scale;

 public:
  tcomp(ang2d a=0) : cart(pt3d(0,0,0)), ang(ang3d(a)), scale(pt3d(1,1,1)) {}
  tcomp(const ang3d& a) : cart(pt3d(0,0,0)), ang(a), scale(pt3d(1,1,1)) {}
  tcomp(const pt3d& c, const ang3d& a=ang3d(0),	const pt3d& s=pt3d(1,1,1))
    : cart(c), ang(a), scale(s) {}
  tcomp(const tmtrx& t);  // Warning: assumes t has unity scaling

  pt3d& Cart() {return cart;}
  ang3d& Ang() {return ang;}
  pt3d& Scale() {return scale;}
  const pt3d& Cart() const {return cart;}
  const ang3d& Ang() const {return ang;}
  const pt3d& Scale() const {return scale;}

  tcomp& operator+=(tcomp& t);
  tcomp& operator-=(tcomp& t);
  tcomp	operator- ()         {return tcomp(-Cart(),-Ang(),-Scale());}
  tcomp	operator+ (tcomp& t) {return tcomp(Cart()+t.Cart(),Ang()+t.Ang(),Scale()+t.Scale());}
  tcomp	operator- (tcomp& t) {return tcomp(Cart()-t.Cart(),Ang()-t.Ang(),Scale()-t.Scale());}
  //tcomp Plus(tcomp&);
  //tcomp Plus2(tcomp&);
  //tcomp Clamped1();
  //tcomp Clamped2();

// replace next two funcs by plain max() & min() someday -PAH
  void	SetMax(tcomp& t) {Cart().SetMax(t.Cart()); Ang().SetMax(t.Ang());}
  void	SetMin(tcomp& t) {Cart().SetMin(t.Cart()); Ang().SetMin(t.Ang());}

  friend ostream& operator<<(ostream&, const tcomp&);
};


//===========================================================================
class tmtrx {
  typedef double fourby3[4][3];
  fourby3 m;

 public:
  tmtrx() {}
  tmtrx(const tmtrx& t) {Copy(t.m);}
  tmtrx(const fourby3 newm) {Copy(newm);}
  tmtrx(const tcomp& t) {SetMtrx(t.Ang(),t.Scale()); Cart()=t.Cart();}
  tmtrx(ang2d a)        {SetMtrx(ang3d(a,0,0)); Cart()= pt3d(0,0,0);}
  tmtrx(const ang3d& a) {SetMtrx(a); Cart()= pt3d(0,0,0);}
  tmtrx(const pt3d& c, const ang3d& a=ang3d(0), const pt3d& sc=pt3d(1,1,1)) {
    SetMtrx(a,sc);
    Cart()=c;
  }

  pt3d&  Cart() const {return *((pt3d*) m[3]);}
  ang3d  Ang() const;
  double *operator[](int i) {return m[i];}
  const double *operator[](int i) const {return m[i];}
  tmtrx& operator= (const tmtrx& t) {if (this != &t) Copy(t.m); return *this;}
  bool	 operator==(const tmtrx& t) const;
  tmtrx  operator* (const tmtrx&) const;
  tmtrx  Inverse() const;
  bool   RotationSame(const tmtrx& t) const;

 private:
  void  SetMtrx(const ang3d& a, const pt3d& s=pt3d(1,1,1));
  void  Copy(const fourby3);

  friend ostream& operator<<(ostream&,const tmtrx&);
};

inline
tmtrx operator*(const tcomp& tc, const tmtrx& tm) {return tmtrx(tc) * tm;}


//===========================================================================

// these could easily be templatized to work with both tmtrx or double[4][4]
pt3d operator*(const pt3d& p, const tmtrx& t);
pt3d operator/(const pt3d& p, const tmtrx& t);
inline pt3d operator>>(const pt3d& p, const tmtrx& t) {return p*t;}
inline pt3d operator<<(const pt3d& p, const tmtrx& t) {return p/t;}


#endif
