////////////////////////////////////////////////////////////
/* vector.cc - general purpose 3d vector class library    */
/* Chris X Edwards                                        */
/* cxe- Sun Feb  3 00:40:14 PST 2002                      */
/* $Date: 2002/05/01 05:07:51 $ - $Revision: 1.2 $  */
////////////////////////////////////////////////////////////

#include <iostream.h>
#include <math.h>

#include "vector.h"

/* FUNCTION DEFINITIONS */
/* -------------------- */
Vector::Vector() 
{ x = 0; y = 0; z = 0; }

Vector::Vector(float X, float Y, float Z)
{ x=X; y=Y; z=Z; }

Vector::~Vector()
{ }

Vector Vector::gg_set2(float a, float b, float c)
{ x=a; y=b; z=c;
  return *this; }

float Vector::gg_valX()
{ return x; }

float Vector::gg_valY()
{ return y; }

float Vector::gg_valZ()
{ return z; }

Vector Vector::gg_inverse() // doesn't affect vector
{ Vector I;
  I.x=-x; I.y=-y; I.z=-z;
  return I; }

Vector Vector::gg_set2_inverse() // modifies vector
{ x=-x; y=-y; z=-z;
  return *this; }

float Vector::gg_magnitude() //a.k.a. radius or distance
{ float m=sqrt(x*x + y*y + z*z);
  return m;}

float Vector::gg_distance(Vector P2) //a.k.a. distance between two points
{ Vector P=(*this-P2);
  return P.gg_magnitude(); }

Vector Vector::gg_direction()
{ float mag;
  Vector unit;
  mag = gg_magnitude(); // this->gg_magnitude() works too...
  if (mag > 0)
    { unit.x = gg_valX() / mag;
      unit.y = gg_valY() / mag;
      unit.z = gg_valZ() / mag; }
  else { unit.x = 0; unit.y = 0; unit.z =0;}
  return unit; }

Vector Vector::gg_set2_direction()
{ float mag;
  mag = gg_magnitude(); // this->gg_magnitude() works too...
  if (mag > 0)
    { x = gg_valX() / mag;
      y = gg_valY() / mag;
      z = gg_valZ() / mag; }
  else { x = 0; y = 0; z =0;}
  return *this; }

Vector Vector::gg_add(Vector offset)
{ Vector sum;
  sum.x = x + offset.x;
  sum.y = y + offset.y;
  sum.z = z + offset.z;
  return sum; }

Vector Vector::gg_set2_add(Vector offset)
{ x += offset.x;
  y += offset.y;
  z += offset.z;
  return *this; }

Vector Vector::gg_subtract(Vector offset)
{ Vector difference;
  difference.x = x - offset.x;
  difference.y = y - offset.y;
  difference.z = z - offset.z;
  return difference; }

Vector Vector::gg_set2_subtract(Vector offset)
{ x -= offset.x;
  y -= offset.y;
  z -= offset.z;
  return *this; }

Vector Vector::gg_rotate(float &rads)
{ Vector rotated;
  float s, c;
  s=sin(rads); c=cos(rads); 
  rotated.x = x*c - y*s;
  rotated.y= y*c + x*s;
  rotated.z= z;
  return rotated;        }

Vector Vector::gg_rotate(float &rads, Vector &basept)
{ Vector rotated;
  float y2, s, c;
  rotated = this->gg_subtract(basept);
  s=sin(rads); c=cos(rads); 
  y2= rotated.y*c + rotated.x*s;
  rotated.x = rotated.x*c - rotated.y*s;
  rotated.y = y2;
  rotated.gg_set2_add(basept);
  return rotated;              }

Vector Vector::gg_set2_rotate(float &rads)
{ float y2, s, c;
  s=sin(rads); c=cos(rads); 
  y2= y*c + x*s;
  x = x*c - y*s;
  y = y2;  
  return *this;   }

Vector Vector::gg_set2_rotate(float &rads, Vector &basept)
{ float y2, s, c;
  this->gg_set2_subtract(basept);
  s=sin(rads); c=cos(rads); 
  y2= y*c + x*s;
  x = x*c - y*s;
  y = y2;
  this->gg_set2_add(basept);
  return *this;              }

/*    Vector gg_tilt(float &rads); // rotation in XZ axis */
Vector Vector::gg_tilt(float &rads)
{ Vector rotated;
  float s, c;
  s=sin(rads); c=cos(rads); 
  rotated.x = x*c - y*s;
  rotated.z= z*c + z*s;
  rotated.y= y;
  return rotated;        }

Vector Vector::gg_tilt(float &rads, Vector &basept)
{ Vector rotated;
  float z2, s, c;
  rotated = this->gg_subtract(basept);
  s=sin(rads); c=cos(rads); 
  z2= rotated.z*c + rotated.x*s;
  rotated.x = rotated.x*c - rotated.z*s;
  rotated.z = z2;
  rotated.gg_set2_add(basept);
  return rotated;              }

Vector Vector::gg_set2_tilt(float &rads)
{ float z2, s, c;
  s=sin(rads); c=cos(rads); 
  z2= z*c + x*s;
  x = x*c - z*s;
  z = z2;  
  return *this;   }

Vector Vector::gg_set2_tilt(float &rads, Vector &basept)
{ float z2, s, c;
  this->gg_set2_subtract(basept);
  s=sin(rads); c=cos(rads); 
  z2= z*c + x*s;
  x = x*c - z*s;
  z = z2;
  this->gg_set2_add(basept);
  return *this;              }

Vector Vector::gg_viewvector(Vector &VV)
{
if ((VV.x != 0) || (VV.y != 0))
{Vector T, Xplus(1,0,0);
 float r, t;
   T.x=VV.x; T.y=VV.y; T.z=0;
   r= ( T.y<0 ? T.gg_angle(Xplus) : -T.gg_angle(Xplus) );   
 t = (M_PI/2) -  atan(VV.z / sqrt(VV.x * VV.x + VV.y * VV.y) );
 T = gg_rotate(r);
 T.gg_set2_tilt(t);
 r=-T.x; T.x=T.y; T.y=r; // turn 90deg- Z always points up
return T;}

else return *this;
}

Vector Vector::gg_set2_viewvector(Vector &VV) 
{ *this = gg_viewvector(VV);
  return *this;
}

Vector Vector::gg_perspective(float &lens, float &camdist)
{ Vector T; float tz;
  tz = camdist - z;
  if (tz) {
  T.x= (x * lens) / tz;
  T.y= (y * lens) / tz;}
return T;}

Vector Vector::gg_set2_perspective(float &lens, float &camdist)
{ *this = gg_perspective(lens, camdist);
  return *this;
}

Vector Vector::gg_scale(float &fact)
{ Vector scaled;
  scaled.x = x*fact;
  scaled.y= y*fact;
  scaled.z= z*fact;
  return scaled;        }

Vector Vector::gg_scale(float &fact, Vector &basept)
{ Vector scaled;
  scaled = this->gg_subtract(basept);
  scaled.x = scaled.x*fact;
  scaled.y = scaled.y*fact;
  scaled.z = scaled.z*fact;
  scaled.gg_set2_add(basept);
  return scaled;              }

Vector Vector::gg_set2_scale(float &fact)
{ x *= fact;
  y *= fact;
  z *= fact;
  return *this;   }

Vector Vector::gg_set2_scale(float &fact, Vector &basept)
{ this->gg_set2_subtract(basept);
  x *= fact;
  y *= fact;
  z *= fact;
  this->gg_set2_add(basept);
  return *this;              }

Vector Vector::gg_scale(Vector &fact3d)
{ Vector scaled;
  scaled.x = x * fact3d.x;
  scaled.y = y * fact3d.y;
  scaled.z = z * fact3d.z;
  return scaled;        }

Vector Vector::gg_scale(Vector &fact3d, Vector &basept)
{ Vector scaled;
  scaled = this->gg_subtract(basept);
  scaled.x = scaled.x * fact3d.x;
  scaled.y = scaled.y * fact3d.y;
  scaled.z = scaled.z * fact3d.z;
  scaled.gg_set2_add(basept);
  return scaled;              }

Vector Vector::gg_set2_scale(Vector &fact3d)
{ x *= fact3d.x;
  y *= fact3d.y;
  z *= fact3d.z;
  return *this;   }

Vector Vector::gg_set2_scale(Vector &fact3d, Vector &basept)
{ this->gg_set2_subtract(basept);
  x *= fact3d.x;
  y *= fact3d.y;
  z *= fact3d.z;
  this->gg_set2_add(basept);
  return *this;              }

float Vector::gg_dot(Vector &B) 
{float dotprod;
 dotprod = x * B.x + y * B.y + z * B.z;
 return dotprod; }

float Vector::gg_angle(Vector &B)
{float ang;
 ang = gg_magnitude() * B.gg_magnitude();
 if ( ang == 0 ) ang = 0;
 else ang = acos( gg_dot(B) / ang ) ;
 if (!(ang == ang)) return 0; /* nan check!!! See note below */
 else return ang; }
/* Whoa, you know you're having a bad day when you use a mind spanker
like this. Basically, by good faith efforts to prevent the random
ravishes of rounding randomness worked only to a point. In this
function, I check for the 0 denomenator. What I don't check for is a
very teensy weeensy denomenator. One so small, in fact, that it's
resulting value is so astronomically high that it can be represented.
This returns a very less than helpful "nan" which is "not a number".
It turns out that you can check for nan by asking if it equals itself
- apparently, nan is so not a number that it doesn't even equal
itself. There are other ways to check for this, but I didn't want to
mess with any more functions though it looks like they're in the math
lib.... The web page I found info at is here: */
/* http://www.gnu.org/manual/glibc/html_chapter/libc_20.html#SEC405 */

Vector Vector::gg_midpoint(Vector &B)
{ return (((*this - B) * .5) + B); }


float Vector::gg_angle(Vector B, Vector basept)
{gg_set2_subtract(basept); B.gg_set2_subtract(basept);
 return gg_angle(B); }

Vector Vector::gg_cross(Vector &B)
{Vector C;
 C.x = y * B.z - z * B.y;
 C.y = z * B.x - x * B.z;
 C.z = x * B.y - y * B.x;
 return C;  }

Vector Vector::gg_set2_cross(Vector &B)
{Vector C;
 C.x = y * B.z - z * B.y;
 C.y = z * B.x - x * B.z;
 C.z = x * B.y - y * B.x;
 x=C.x; y=C.y; z=C.z;
 return *this;  }

Vector operator!(Vector A)            // invert
{ return A.gg_inverse(); }

Vector operator+(Vector A, Vector B)  // add
{ return A.gg_add(B); }
Vector operator+=(Vector &A, Vector B) // set 2 add
{ return A.gg_set2_add(B); }

Vector operator-(Vector A, Vector B)  // subtract
{ return A.gg_subtract(B); }
Vector operator-=(Vector &A, Vector B) // set 2 subtract
{ return A.gg_set2_subtract(B); }

Vector operator%(Vector A, float angle )  // rotate
{ return A.gg_rotate(angle); }
Vector operator%=(Vector &A, float angle) // set 2 rotate
{ return A.gg_set2_rotate(angle); }

Vector operator*(Vector A, float B)  // scale
{ return A.gg_scale(B); }
Vector operator*=(Vector &A, float B) // set 2 scale
{ return A.gg_set2_scale(B); }
Vector operator*(Vector A, Vector B)  // scale3d
{ return A.gg_scale(B); }
Vector operator*=(Vector &A, Vector B) // set 2 scale3d
{ return A.gg_set2_scale(B); }

float operator|(Vector A, Vector B) // dot product
{ return A.gg_dot(B); }
Vector operator&(Vector A, Vector B) // cross product
{ return A.gg_cross(B); }
Vector operator&=(Vector &A, Vector B) // cross product
{ return A.gg_set2_cross(B); }


bool Vector::compareto(Vector Q)
{ const float RES=.0000005;
  if ( ( fabs(Q.x-x) < RES ) 
      && ( fabs(Q.y-y) < RES )
        && ( fabs(Q.z-z) < RES ) )
          return 1; /* Good - they're effectively the same */
  else
     return 0; /* Distinct - fail, they're different enough */
}

bool operator==(Vector A, Vector B)
{ return A.compareto(B); }

void Vector::print(ostream *os)
{ *os << "(" << x << "," << y << "," << z << ")"; }

ostream &operator<<(ostream &os, Vector p)
{ p.print(&os);
return os; }

