//****************************************************************************//
// vector.cpp                                                                 //
// Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger                       //
//****************************************************************************//
// This library is free software; you can redistribute it and/or modify it    //
// under the terms of the GNU Lesser General Public License as published by   //
// the Free Software Foundation; either version 2.1 of the License, or (at    //
// your option) any later version.                                            //
//****************************************************************************//

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

//****************************************************************************//
// Includes                                                                   //
//****************************************************************************//

#include "cal3d/vector.h"
#include "cal3d/matrix.h"
#include "cal3d/quaternion.h"

 /*****************************************************************************/
/** Constructs the vector instance.
  *
  * This function is the default constructor of the vector instance.
  *****************************************************************************/

CalVector::CalVector()
  : x(0.0f), y(0.0f), z(0.0f)
{
}

 /*****************************************************************************/
/** Constructs the vector instance.
  *
  * This function is a constructor of the vector instance.
  *
  * @param v The vector to construct this vector instance from.
  *****************************************************************************/

CalVector::CalVector(const CalVector& v)
  : x(v.x), y(v.y), z(v.z)
{
}

 /*****************************************************************************/
/** Constructs the vector instance.
  *
  * This function is a constructor of the vector instance.
  *
  * @param vx The x component.
  * @param vy The y component.
  * @param vz The z component.
  *****************************************************************************/

CalVector::CalVector(float vx, float vy, float vz)
  : x(vx), y(vy), z(vz)
{
}

 /*****************************************************************************/
/** Destructs the vector instance.
  *
  * This function is the destructor of the vector instance.
  *****************************************************************************/

CalVector::~CalVector()
{
}

 /*****************************************************************************/
/** Provides access to the components of the vector instance.
  *
  * This function provides read and write access to the three components of the
  * vector instance.
  *
  * @param i The index to the specific component.
  *
  * @return A reference to the specific component.
  *****************************************************************************/

float& CalVector::operator[](unsigned int i)
{
  return (&x)[i];
}

 /*****************************************************************************/
/** Provides access to the components of the vector instance.
  *
  * This function provides read access to the three components of the vector
  * instance.
  *
  * @param i The index to the specific component.
  *
  * @return A constant reference to the specific component.
  *****************************************************************************/

const float& CalVector::operator[](unsigned int i) const
{
  return (&x)[i];
}

 /*****************************************************************************/
/** Equates the vector instance with another vector.
  *
  * This operator equates the vector instance with another vector.
  *
  * @param v The vector to equate the vector instance with.
  *****************************************************************************/

void CalVector::operator=(const CalVector& v)
{
  x = v.x;
  y = v.y;
  z = v.z;
}

 /*****************************************************************************/
/** Adds another vector to the vector instance.
  *
  * This operator adds another vector to the vector instance.
  *
  * @param v The vector to be added.
  *****************************************************************************/

void CalVector::operator+=(const CalVector& v)
{
  x += v.x;
  y += v.y;
  z += v.z;
}

 /*****************************************************************************/
/** Subtracts another vector from the vector instance.
  *
  * This operator subtracts another vector from the vector instance.
  *
  * @param v The vector to be subtracted.
  *****************************************************************************/

void CalVector::operator-=(const CalVector& v)
{
  x -= v.x;
  y -= v.y;
  z -= v.z;
}

 /*****************************************************************************/
/** Scales the vector instance.
  *
  * This operator scales the vector instance by multiplying its components by
  * a specific factor.
  *
  * @param d The factor to multiply the vector components by.
  *****************************************************************************/

void CalVector::operator*=(const float d)
{
  x *= d;
  y *= d;
  z *= d;
}

 /*****************************************************************************/
/** Transforms the vector instance by a quaternion.
  *
  * This function transforms the vector instance by a given quaternion.
  *
  * @param q The quaternion to be used for the transformation.
  *****************************************************************************/

void CalVector::operator*=(const CalQuaternion& q)
{
  CalQuaternion temp(-q.x, -q.y, -q.z, q.w);
  temp *= *this;
  temp *= q;

  x = temp.x;
  y = temp.y;
  z = temp.z;
}

 /*****************************************************************************/
/** Transforms the vector instance by a matrix.
  *
  * This function transforms the vector instance by a given matrix.
  *
  * @param m The matrix to be used for the transformation.
  *****************************************************************************/

void CalVector::operator*=(const CalMatrix& m)
{
  float ox = x;
  float oy = y;
  float oz = z;
  x = m.dxdx*ox + m.dxdy*oy + m.dxdz*oz;
  y = m.dydx*ox + m.dydy*oy + m.dydz*oz;
  z = m.dzdx*ox + m.dzdy*oy + m.dzdz*oz;
}

 /*****************************************************************************/
/** Scales the vector instance.
  *
  * This operator scales the vector instance by dividing its components by a
  * specific factor.
  *
  * @param d The factor to divide the vector components by.
  *****************************************************************************/

void CalVector::operator/=(const float d)
{
  x /= d;
  y /= d;
  z /= d;
}

 /*****************************************************************************/
/** Tests the equality of 2 vectors
  *
  * This operator checks to see if 2 vectors are equal
  *
  * @param v The vector to be tested against.
  *****************************************************************************/

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

 /*****************************************************************************/
/** Calculates the sum of two vectors.
  *
  * This operator calculates the sum of two vectors.
  *
  * @param v The first vector to be added.
  * @param u The second vector to be added.
  *
  * @return The sum of the two vectors.
  *****************************************************************************/

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

 /*****************************************************************************/
/** Calculates the difference of two vectors.
  *
  * This operator calculates the difference of two vectors.
  *
  * @param v The first vector to be added.
  * @param u The second vector to be subtracted.
  *
  * @return The difference of the two vectors.
  *****************************************************************************/

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

 /*****************************************************************************/
/** Calculates a scaled vector.
  *
  * This operator calculates the vector multiplied by a factor.
  *
  * @param v The vector to be scaled.
  * @param d The factor to multiply the vector with.
  *
  * @return The scaled vector.
  *****************************************************************************/

CalVector operator*(const CalVector& v, float d)
{
  return CalVector(v.x * d, v.y * d, v.z * d);
}

 /*****************************************************************************/
/** Calculates a scaled vector.
  *
  * This operator calculates the vector multiplied by a factor.
  *
  * @param d The factor to multiply the vector with.
  * @param v The vector to be scaled.
  *
  * @return The scaled vector.
  *****************************************************************************/

CalVector operator*(float d, const CalVector& v)
{
  return CalVector(v.x * d, v.y * d, v.z * d);
}

 /*****************************************************************************/
/** Calculates a scaled vector.
  *
  * This operator calculates the vector divided by a factor.
  *
  * @param v The vector to be scaled.
  * @param d The factor to divide the vector with.
  *
  * @return The scaled vector.
  *****************************************************************************/

CalVector operator/(const CalVector& v, float d)
{
  return CalVector(v.x / d, v.y / d, v.z / d);
}

 /*****************************************************************************/
/** Calculates the dot product of two vectors.
  *
  * This operator calculates the dot product of two vectors.
  *
  * @param v The first vector.
  * @param u The second vector.
  *
  * @return The dot product of the two vectors.
  *****************************************************************************/

float operator*(const CalVector& v, const CalVector& u)
{
  return v.x * u.x + v.y * u.y + v.z * u.z;
}

 /*****************************************************************************/
/** Calculates the vector product of two vectors.
  *
  * This operator calculates the vector product of two vectors.
  *
  * @param v The first vector.
  * @param u The second vector.
  *
  * @return The vector product of the two vectors.
  *****************************************************************************/

CalVector operator%(const CalVector& v, const CalVector& u)
{
  return CalVector(v.y * u.z - v.z * u.y, v.z * u.x - v.x * u.z, v.x * u.y - v.y * u.x);
}

 /*****************************************************************************/
/** Interpolates the vector instance to another vector.
  *
  * This function interpolates the vector instance to another vector by a given
  * factor.
  *
  * @param d The blending factor in the range [0.0, 1.0].
  * @param v The vector to be interpolated to.
  *****************************************************************************/

void CalVector::blend(float d, const CalVector& v)
{
  x += d * (v.x - x);
  y += d * (v.y - y);
  z += d * (v.z - z);
}

 /*****************************************************************************/
/** Clears the vector instance.
  *
  * This function clears the vector instance.
  *****************************************************************************/

void CalVector::clear()
{
  x = 0.0f;
  y = 0.0f;
  z = 0.0f;
}

 /*****************************************************************************/
/** Returns the length of the vector instance.
  *
  * This function returns the length of the vector instance.
  *
  * @return The length of the vector instance.
  *****************************************************************************/

float CalVector::length()
{
  return (float)sqrt(x * x + y * y + z * z);
}

 /*****************************************************************************/
/** Normalizes the vector instance.
  *
  * This function normalizes the vector instance and returns its former length.
  *
  * @return The length of the vector instance before normalizing.
  *****************************************************************************/

float CalVector::normalize()
{
  // calculate the length of the vector
  float length;
  length = (float) sqrt(x * x + y * y + z * z);

  // normalize the vector
  x /= length;
  y /= length;
  z /= length;

  return length;
}

 /*****************************************************************************/
/** Sets new values.
  *
  * This function sets new values in the vector instance.
  *
  * @param x The x component.
  * @param y The y component.
  * @param z The z component.
  *****************************************************************************/

void CalVector::set(float vx, float vy, float vz)
{
  x = vx;
  y = vy;
  z = vz;
}

 /*****************************************************************************
  *
  *   Functions for the plane class, they don't have real sense outside
  *   bounding box calculation,
  *   
  *****************************************************************************/
  
  
float CalPlane::eval(CalVector &p)
{
   return p.x*a+p.y*b+p.z*c+d;
}

void CalPlane::setPosition(CalVector &p)
{
   d=-p.x*a-p.y*b-p.z*c;
}

void CalPlane::setNormal(CalVector &p)
{
     a=p.x;
     b=p.y;
     c=p.z;
     d=-1e32f;
};


 /*****************************************************************************/
/** Computes points of a bounding box.
  *
  * This function computes the 8 points of a bounding box.
  *
  * @param p A pointer to CalVector[8], the 8 points of the bounding box
  *****************************************************************************/


void CalBoundingBox::computePoints(CalVector *p)
{
    CalMatrix m;
     
    int i,j,k;
        
    for(i=0;i<2;i++)
       for(j=2;j<4;j++)
           for(k=4;k<6;k++)
        {
           float x,y,z;
           
           m.dxdx=plane[i].a;m.dxdy=plane[i].b;m.dxdz=plane[i].c;        
           m.dydx=plane[j].a;m.dydy=plane[j].b;m.dydz=plane[j].c;        
           m.dzdx=plane[k].a;m.dzdy=plane[k].b;m.dzdz=plane[k].c;
           
           float det = m.det();
           
           if(det!=0)
           {
              m.dxdx=-plane[i].d;m.dxdy=plane[i].b;m.dxdz=plane[i].c;        
              m.dydx=-plane[j].d;m.dydy=plane[j].b;m.dydz=plane[j].c;        
              m.dzdx=-plane[k].d;m.dzdy=plane[k].b;m.dzdz=plane[k].c;
              
              x=m.det()/det;

              m.dxdx=plane[i].a;m.dxdy=-plane[i].d;m.dxdz=plane[i].c;        
              m.dydx=plane[j].a;m.dydy=-plane[j].d;m.dydz=plane[j].c;        
              m.dzdx=plane[k].a;m.dzdy=-plane[k].d;m.dzdz=plane[k].c;
              
              y=m.det()/det;

              m.dxdx=plane[i].a;m.dxdy=plane[i].b;m.dxdz=-plane[i].d;        
              m.dydx=plane[j].a;m.dydy=plane[j].b;m.dydz=-plane[j].d;        
              m.dzdx=plane[k].a;m.dzdy=plane[k].b;m.dzdz=-plane[k].d;
  
              z=m.det()/det;
              
              p->x=x;p->y=y;p->z=z;
           }
           else {p->x=0.0f;p->y=0.0f;p->z=0.0f;}
           
           p++;           
        }           
 }
   



//****************************************************************************//
