/* (c) 1999-2000 Tino Schwarze, see COPYING for details */
/**@pkg math*/
/**
 * class for a 3D vertex
 *
 * #include "cVertex.hh"
 *
 * This class provides a 3D vertex and several operations on it.
 * (e.g. adding, scaling, dot product)
 *
 * @see cMatrix
 * @see cQuaternion
 */

#ifndef cVertex_hh
#define cVertex_hh

// PROJECT INCLUDES
#include "common.hh"


/**
 * A class for easy matrix math.
 *
 * Always remember: This class might be used a lot and during time-critical
 * operations. Thus, it's optimized for speed, which has the
 * consequence that it cannot be derived from this class.
 */
class cVertex
{
public:

// LIFECYCLE
	/**
	 * default constructor (initializes to zero)
		*/
	cVertex ()	
	{
		ENTER_METHOD("cVertex::cVertex()");
		SetZero ();
	};

	// copy construktor is provided by compiler

	/**
	 * constructor accepting a vertex split into it's components
	 	* @param vx n.x component
		* @param vy n.y component
		* @param vz n.z component
		*/
    cVertex (GLfloat vx, GLfloat vy, GLfloat vz)
    {
		ENTER_METHOD("cVertex::cVertex (vx,vy,vz)");
		n.x = vx; n.y = vy; n.z = vz;
	} ;

	// destructor is provided by compiler
#if DEBUG
	/**
	 * destructor (for debugging purposes only)
	 	*/
	~cVertex ()
	{
		ENTER_METHOD("cVertex::~cVertex()");
	}
#endif

// OPERATORS
	/**
	 * operator for scaling by a given value
	 	* @param s scale factor
		*/
	inline cVertex &operator *= (const GLfloat s)
	{
		n.x *= s; n.y *= s; n.z *= s;
        return *this;
	};

	/**
	 * operator for adding two cVertex objects
	 	* @param v cVertex to add
		*/
	inline cVertex &operator += (const cVertex &v)
	{
		n.x += v.n.x; n.y += v.n.y; n.z += v.n.z;
		return *this;
	};

	/**
	 * operator for subtracting two cVertex objects
	 	* @param v cVertex to subtract
		*/
	inline cVertex &operator -= (const cVertex &v)
	{
		n.x -= v.n.x; n.y -= v.n.y; n.z -= v.n.z;
		return *this;
	};

	/**
	 * operator for multiplying any cVertex with a scalar
	 	* @param s value to scale by
		*/
	inline cVertex operator * (const GLfloat s) const
	{
        return cVertex (s*n.x, s*n.y, s*n.z);
	};

	/**
	 * operator to add two cVertex objects
	 	* @param v cVertex object to add
		*/
	inline cVertex operator + (const cVertex &v) const
	{
		return cVertex(n.x+v.n.x, n.y+v.n.y, n.z+v.n.z);
	};

	/**
	 * operator for subtracting two cVertex objects
	 	* @param v cVertex object to substract
		*/
	inline cVertex operator - (const cVertex &v) const
	{
		return cVertex(n.x-v.n.x, n.y-v.n.y, n.z-v.n.z);
	};

	/**
	 * operator to multiply two cVertex objects (dot product)
	 	* @param v cVertex to calculate dot product with
		*
		* @return dot product of the two cVertex's
		*/
	inline GLfloat operator * (const cVertex &v) const // dot product
	{
		return (n.x*v.n.x + n.y*v.n.y + n.z*v.n.z);
	}

	/**
	 * operator to retrieve a component of an cVertex by index
	 	* @param idx index of value (0 = x, 1 = y, 2 = z)
		*/
	inline GLfloat &operator[] (const int idx)
	{
		return a[idx];
	}

	/**
	 * converting to GLfloat *
	 	*/
	inline operator GLfloat *()
	{
		return (GLfloat *)&a;
	}

// OPERATIONS

	/**
	 * assign a new value to all three components (it's basically
	 * superfluous)
		* @param vx n.x component
		* @param vy n.y component
		* @param vz n.z component
		*/
	inline cVertex Assign (GLfloat vx, GLfloat vy, GLfloat vz)
	{
		n.x = vx; n.y = vy; n.z = vz;
		return *this;
	};

	/**
	 * normalize the vertex (make it length 1)
	 	* @return reference to normalized cVertex
	 	*/
	inline cVertex &Normalize ()
	{
		const GLfloat l = GetLength ();

		if (l == 0) return *this;	// it's zero anyway, so don't touch it

		// first calc 1/l
		const GLfloat one_by_l = 1.0/l;

		// then multiply (should actually save some cycles)
		(*this) *= one_by_l;

		return *this;
	};

	/**
	 * convenience function: translate by given vertex
	 	* @param tx n.x component
	 	* @param ty n.y component
	 	* @param tz n.z component
		*/
	inline void Translate (GLfloat tx, GLfloat ty, GLfloat tz)
	{ 
		n.x += tx; n.y += ty; n.z += tz; 
	};


	/**
	 * invert direction of vector by inverting all its components
	 	* @returns reference to inverted cVertex
	 	*/
	inline cVertex &Invert ()
	{
		n.x = -n.x; n.y = -n.y; n.z = -n.z;
		return *this;
	}


	/**
	 * calculate cross product of two given vertices
	 	* @param v1 first vertex
		* @param v2 second vertex
		*
		* @returns cVertex object
		*/
	inline cVertex CrossProd (const cVertex &v1, const cVertex &v2) const
	{
		return cVertex (v1.n.y*v2.n.z - v1.n.z*v2.n.y,
                        v1.n.z*v2.n.x - v1.n.x*v2.n.z,
                        v1.n.x*v2.n.y - v1.n.y*v2.n.x);
	}

	/**
	 * calculate cross product (does not change class)
	 	* @param v vector to calculate cross product with
		*
		* @returns a cVertex object
		*/
	inline cVertex CrossProd (const cVertex &v) const
	{
		return cVertex (n.y*v.n.z - n.z*v.n.y,
                        n.z*v.n.x - n.x*v.n.z,
                        n.x*v.n.y - n.y*v.n.x);
	}

// ACCESS
	/**
	 * set vertex to zero vector
	 	*/
	inline void SetZero () { n.x = n.y = n.z = 0; };

	/**
	 * query current x component of vector
		* @returns GLfloat - value of x component
		*/
	inline GLfloat GetX () const {
		return n.x;
	}

	/**
	 * query current y component of vector
		* @returns GLfloat - value of y component
		*/
	inline GLfloat GetY () const {
		return n.y;
	}

	/**
	 * query current z component of vector
		* @returns GLfloat - value of z component
		*/
	inline GLfloat GetZ () const {
		return n.z;
	}

	/**
	 * determine length of vertex
		* @returns length of vertex
		*/
	inline GLfloat GetLength () const
	{
		return sqrt (n.x*n.x + n.y*n.y + n.z*n.z);
	};

private:
	// I was tempted to make this public but I decided against it for the
	// sake of a cleander design. Sigh.

	/**
	 * Anonymous union used to store the vector components.
	 * By using this union, we're able to access the vector by
	 * component names or component indices.*/
	union {
		struct {
			GLfloat x, y, z;	
		} n;
		GLfloat a[3];
	};

};



#endif // ifndef cVertex_hh
/* vim:ts=4:smartindent:
*/

