/* vector.H: all about vectors (in C++) */

#ifndef _XRML_VECTOR_H_
#define _XRML_VECTOR_H_

#include <math.h>
#include "Float.H"
#include "xrml.H"

namespace xrml {

/* a vector in 3D space */
struct Vec3 /*: public PooledObj*/ {
  float x, y, z;

  inline Vec3(float xx =0., float yy =0., float zz =0.) { x=xx; y=yy; z=zz; }
  inline Vec3(SFVec3f v) { x=v.x; y=v.y; z=v.z; }
  Vec3(const class Vec4&);	// at the bottom of this file

  inline operator SFVec3f() const {return SFVec3f(x,y,z); }
  
  inline Float tolerance() const { return EPSILON*(fabs(x) + fabs(y) + fabs(z)); }

  inline bool operator==(const Vec3& v) const 
  { return x==v.x && y==v.y && z==v.z; }

  inline bool operator!=(const Vec3& v) const
  { return x!=v.x || y!=v.y || z!=v.z; }

  inline bool operator|=(Float eps) const
  { return ((Float)x|=eps) && ((Float)y|=eps) && ((Float)z|=eps); }

  inline Vec3 operator<<(const Vec3& v) const
  { return Vec3((Float)x<<(Float)v.x, (Float)y<<(Float)v.y, (Float)z<<(Float)v.z); }

  inline Vec3 operator>>(const Vec3& v) const
  { return Vec3((Float)x>>(Float)v.x, (Float)y>>(Float)v.y, (Float)z>>(Float)v.z); }

  inline Vec3& operator<<=(const Vec3& v)
  { 
    x = (Float)x<<(Float)v.x; 
    y = (Float)y<<(Float)v.y; 
    z = (Float)z<<(Float)v.z; 
    return *this; 
  }

  inline Vec3& operator>>=(const Vec3& v)
  {
    x = (Float)x>>(Float)v.x; 
    y = (Float)y>>(Float)v.y; 
    z = (Float)z>>(Float)v.z; 
    return *this; 
  }

  inline Vec3 operator+(const Vec3& v) const
  { return Vec3(x+v.x, y+v.y, z+v.z); }

  inline Vec3& operator+=(const Vec3& v)
  { x+=v.x; y+=v.y; z+=v.z; return *this; }

  inline Vec3 operator-(const Vec3& v) const
  { return Vec3(x-v.x, y-v.y, z-v.z); }

  inline Vec3& operator-=(const Vec3& v)
  { x-=v.x; y-=v.y; z-=v.z; return *this; }

  inline Vec3 operator-(void) const
  { return Vec3(-x, -y, -z); }

  inline Vec3 operator*(const float s) const
  { return Vec3(x*s, y*s, z*s); }

  inline Vec3& operator*=(const float s)
  { x*=s; y*=s; z*=s; return *this; }

  inline Vec3 operator*(const Vec3& v) const
  { return Vec3(x*v.x, y*v.y, z*v.z); }

  inline Vec3& operator*=(const Vec3& v)
  { x*=v.x; y*=v.y; z*=v.z; return *this; }

  // defined in matrix.H
  const Vec3 operator*(const class Mat3& m) const;
  const Vec3& operator*=(const class Mat3& m);
  const Vec3 operator*(const class Mat4& m) const;
  const Vec3& operator*=(const class Mat4& m);

  inline Vec3 operator/(const float s) const
  { return Vec3(x/s, y/s, z/s); }

  inline Vec3& operator/=(const float s)
  { x/=s; y/=s; z/=s; return *this; }

  inline Vec3 operator/(const Vec3& v) const
  { return Vec3(x/v.x, y/v.y, z/v.z); }

  inline Vec3& operator/=(const Vec3& v)
  { x/=v.x; y/=v.y; z/=v.z; return *this; }

  inline Float operator&(const Vec3& v) const
  { return x*v.x + y*v.y + z*v.z; }

  inline Float norm(void) const
  { return sqrt(x*x + y*y + z*z); }

  inline Vec3& normalize(void)
  {
    double norm = sqrt(x*x + y*y + z*z); 
    if (norm > EPSILON) 	// avoid division by zero
      { x /= norm; y /= norm; z /= norm; }
    return *this;
  }

  inline Vec3 operator^(const Vec3& v) const
  {
    Vec3 a(*this), b(v);
    return Vec3(a.y * b.z - a.z * b.y,
		a.z * b.x - a.x * b.z,
		a.x * b.y - a.y * b.x);
  }

  inline Vec3& operator^=(const Vec3& v)
  {
    Vec3 a(*this), b(v);
    x = a.y * b.z - a.z * b.y;
    y = a.z * b.x - a.x * b.z;
    z = a.x * b.y - a.y * b.x;
    return *this;
  }

  friend ostream& operator<<(ostream& s, const Vec3& v)
    {
      return s << v.x << ' ' << v.y << ' ' << v.z;
    }
};

// Vector in 4D space: used for both rotation representation 
// and for homogenous 3D coordinates.
struct Vec4: public PooledObj {
  float x, y, z, w;

  inline Vec4(float xx =0., float yy =0., float zz =0., float ww =0.) 
  { x=xx; y=yy; z=zz; w=ww; }

  // constructs a homogenous 3D vector from a "normal" 3D vector.
  inline Vec4(const Vec3& v) { x = v.x; y = v.y; z = v.z; w = 1.; }

  inline Vec4(const SFRotation& r) { x=r.x; y=r.y; z=r.z; w=r.radians; }

  //inline Vec4& operator=(const Vec4& src) 
  //{ x=src.x; y=src.y; z=src.z; w=src.w; return *this; }

  inline float tolerance() const 
  { return EPSILON*(fabs(x) + fabs(y) + fabs(z) + fabs(w)); }

  inline bool operator==(const Vec4& v) const
  { return x==v.x && y==v.y && z==v.z && w==v.w; }

  inline bool operator!=(const Vec4& v) const
  { return x!=v.x || y!=v.y || z!=v.z || w!=v.w; }

  inline const Vec4 operator-(void) const
  { return Vec4(x, y, z, -w); }

  // defined in matrix.H
  const Vec4 operator*(const class Mat4& m) const;
  const Vec4& operator*=(const class Mat4& m);

  friend ostream& operator<<(ostream& s, const Vec4& v)
  {
    return s << v.x << ' ' << v.y << ' ' << v.z << ' ' << v.w;
  }
};

// constructs a Vec3 from a homogenous 3D Vec4 vector
inline Vec3::Vec3(const Vec4& v) { x = v.x / v.w; y = v.y / v.w; z = v.z / v.w; }


/* a vector in 2D space */
struct Vec2 : public PooledObj {
  float u, v;

  inline Vec2(float uu =0., float vv =0.) { u=uu; v=vv; }
  inline Vec2(SFVec2f V) { u=V.s; v=V.t; }

  inline operator SFVec2f() const {return SFVec2f(u,v); }
  
  inline Float tolerance() const { return EPSILON*(fabs(u) + fabs(v)); }

  inline bool operator==(const Vec2& V) const 
  { return u==V.u && v==V.v; }

  inline bool operator!=(const Vec2& V) const
  { return u!=V.u || v!=V.v; }

  inline bool operator|=(Float eps) const
  { return ((Float)u|=eps) && ((Float)v|=eps); }

  inline Vec2 operator<<(const Vec2& V) const
  { return Vec2((Float)u<<(Float)V.u, (Float)v<<(Float)V.v); }

  inline Vec2 operator>>(const Vec2& V) const
  { return Vec2((Float)u>>(Float)V.u, (Float)v>>(Float)V.v); }

  inline Vec2& operator<<=(const Vec2& V)
  { 
    u = (Float)u<<(Float)V.u; 
    v = (Float)v<<(Float)V.v; 
    return *this; 
  }

  inline Vec2& operator>>=(const Vec2& V)
  {
    u = (Float)u>>(Float)V.u; 
    v = (Float)v>>(Float)V.v; 
    return *this; 
  }

  inline Vec2 operator+(const Vec2& V) const
  { return Vec2(u+V.u, v+V.v); }

  inline Vec2& operator+=(const Vec2& V)
  { u+=V.u; v+=V.v; return *this; }

  inline Vec2 operator-(const Vec2& V) const
  { return Vec2(u-V.u, v-V.v); }

  inline Vec2& operator-=(const Vec2& V)
  { u-=V.u; v-=V.v; return *this; }

  inline Vec2 operator-(void) const
  { return Vec2(-u, -v); }

  inline Vec2 operator*(const float s) const
  { return Vec2(u*s, v*s); }

  inline Vec2& operator*=(const float s)
  { u*=s; v*=s; return *this; }

  inline Vec2 operator*(const Vec2& V) const
  { return Vec2(u*V.u, v*V.v); }

  inline Vec2& operator*=(const Vec2& V)
  { u*=V.u; v*=V.v; return *this; }


  inline Vec2 operator/(const float s) const
  { return Vec2(u/s, v/s); }

  inline Vec2& operator/=(const float s)
  { u/=s; v/=s; return *this; }

  inline Vec2 operator/(const Vec2& V) const
  { return Vec2(u/V.u, v/V.v); }

  inline Vec2& operator/=(const Vec2& V)
  { u/=V.u; v/=V.v; return *this; }

  inline Float operator&(const Vec2& V) const
  { return u*V.u + v*V.v; }

  inline Float norm(void) const
  { return sqrt(u*u + v*v); }

  inline Vec2& normalize(void)
  {
    double norm = sqrt(u*u + v*v); 
    if (norm > EPSILON) 	// avoid division by zero
      { u /= norm; v /= norm;}
    return *this;
  }

  inline float operator^(const Vec2& V) const
  {
    return u * V.v - v * V.u;
  }

  friend ostream& operator<<(ostream& s, const Vec2& V)
    {
      return s << V.u << ' ' << V.v;
    }
};


}  // namespace xrml

#endif /* _XRML_VECTOR_H_ */
