/***************************************************************************
* Vec2.h                                                                   *
*                                                                          *
* Basic operations on 2-dimensional vectors.  This special case is useful  *
* because nearly all operations are performed inline.                      *
*                                                                          *
*   HISTORY                                                                *
*      Name    Date        Description                                     *
*                                                                          *
*      arvo    05/22/98    Added TimedVec2, extending Vec2.                *
*      arvo    06/17/93    Initial coding.                                 *
*                                                                          *
*--------------------------------------------------------------------------*
* Copyright (C) 1999, James Arvo                                           *
*                                                                          *
* This program is free software; you can redistribute it and/or modify it  *
* under the terms of the GNU General Public License as published by the    *
* Free Software Foundation.  See http://www.fsf.org/copyleft/gpl.html      *
*                                                                          *
* This program is distributed in the hope that it will be useful, but      *
* WITHOUT EXPRESS OR IMPLIED WARRANTY of merchantability or fitness for    *
* any particular purpose.  See the GNU General Public License for more     *
* details.                                                                 *
*                                                                          *
***************************************************************************/
#ifndef __VEC2_INCLUDED__
#define __VEC2_INCLUDED__

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

namespace ArvoMath {

	class Vec2;       // 2-D floating-point vector.
	class TimedVec2;  // 2-D vector with a time stamp.
	class Mat2x2;     // 2x2 floating-point matrix.

	class Vec2 {
	public:
		Vec2(                  ) { x = 0.0;   y = 0.0;   }
		Vec2( float a, float b ) { x = a;     y = b;     }
		Vec2( const Vec2 &A    ) { x = A.X(); y = A.Y(); }
		~Vec2() {}
		Vec2 &operator=( float s       ) { return Set(     s,     s ); }
		Vec2 &operator=( const Vec2 &A ) { return Set( A.X(), A.Y() ); }
		float  X() const { return x; }
		float  Y() const { return y; }
		float &X()       { return x; }
		float &Y()       { return y; }
		float  operator[]( int i ) const { return *( &x + i ); }
		float &operator[]( int i )       { return *( &x + i ); }
		Vec2  &Set( float a, float b ) { x = a; y = b; return *this; }
		Vec2  &Set( const Vec2 &A    ) { return Set( A.X(), A.Y() ); }
	public:
		static const Vec2 Zero;
		static const Vec2 Xaxis;
		static const Vec2 Yaxis;
	protected:
		float x, y;
	};

	// This class simply adds a time field to the Vec2 class so that time-stamped
	// coordinates can be easily inserted into objects such as Polylines.

	class TimedVec2 : public Vec2 {
	public:
		TimedVec2() { time = 0; }
		TimedVec2( const Vec2 &p   , long u = 0 ) { Set( p ); time = u; }
		TimedVec2( float x, float y, long u = 0 ) { Set(x,y); time = u; }
		~TimedVec2() {}
		Vec2 &Coord()       { return *this; }
		Vec2  Coord() const { return *this; }
		long  Time () const { return  time; }
		void  SetTime( long u ) { time = u; }
	protected:
		long time;
	};

	class Mat2x2 {
	public:
		Mat2x2( ) { Set( 0, 0, 0, 0 ); }
		Mat2x2( float a, float b, float c, float d ) { Set( a, b, c, d ); }
		Mat2x2( const Vec2 &c1, const Vec2 &c2 );
		~Mat2x2( ) {}
		Mat2x2 &operator*=( float scale );
		Mat2x2  operator* ( float scale ) const;
		void Set( float a, float b, float c, float d ) 
		{ m[0][0] = a; m[0][1] = b; m[1][0] = c; m[1][1] = d; }
		float  operator()( int i, int j ) const { return m[i][j]; }
		float &operator()( int i, int j )       { return m[i][j]; }
	private:
		float m[2][2];
	};


	//==========================================
	//===  Miscellaneous external functions  ===                        
	//==========================================

	extern float Normalize( Vec2 &A );
	extern Vec2  Min ( const Vec2 &A, const Vec2 &B );
	extern Vec2  Max ( const Vec2 &A, const Vec2 &B );


	//==========================================
	//===  Norm-related functions           ===                        
	//==========================================

	inline double LenSqr ( const Vec2 &A ) { return Sqr(A[0]) + Sqr(A[1]); }
	inline double Len    ( const Vec2 &A ) { return sqrt( LenSqr( A ) ); }
	inline double OneNorm( const Vec2 &A ) { return Abs( A.X() ) + Abs( A.Y() ); }
	inline double TwoNorm( const Vec2 &A ) { return Len(A); }
	inline float  SupNorm( const Vec2 &A ) { return MaxAbs( A.X(), A.Y() ); }


	//==========================================
	//===  Addition                          ===                        
	//==========================================

	inline Vec2 operator+( const Vec2 &A, const Vec2 &B )
	{
		return Vec2( A.X() + B.X(), A.Y() + B.Y() );
	}

	inline Vec2& operator+=( Vec2 &A, const Vec2 &B )
	{
		A.X() += B.X();
		A.Y() += B.Y();
		return A;
	}


	//==========================================
	//===  Subtraction                       ===                        
	//==========================================

	inline Vec2 operator-( const Vec2 &A, const Vec2 &B )
	{
		return Vec2( A.X() - B.X(), A.Y() - B.Y() );
	}

	inline Vec2 operator-( const Vec2 &A )
	{
		return Vec2( -A.X(), -A.Y() );
	}

	inline Vec2& operator-=( Vec2 &A, const Vec2 &B )
	{
		A.X() -= B.X();
		A.Y() -= B.Y();
		return A;
	}


	//==========================================
	//===  Multiplication                    ===                        
	//==========================================

	inline Vec2 operator*( float c, const Vec2 &A )
	{
		return Vec2( c * A.X(), c * A.Y() );
	}

	inline Vec2 operator*( const Vec2 &A, float c )
	{
		return Vec2( c * A.X(), c * A.Y() );
	}

	inline float operator*( const Vec2 &A, const Vec2 &B )  // Inner product
	{
		return A.X() * B.X() + A.Y() * B.Y();
	}

	inline Vec2& operator*=( Vec2 &A, float c )
	{
		A.X() *= c;
		A.Y() *= c;
		return A;
	}

	//==========================================
	//===  Division                          ===                        
	//==========================================

	inline Vec2 operator/( const Vec2 &A, float c )
	{
		return Vec2( A.X() / c, A.Y() / c );
	}

	inline Vec2 operator/( const Vec2 &A, const Vec2 &B ) 
	{
		return A - B * (( A * B ) / LenSqr( B ));
	}


	//==========================================
	//===  Comparison                        ===                        
	//==========================================

	inline int operator==( const Vec2 &A, const Vec2 &B ) 
	{ 
		return A.X() == B.X() && A.Y() == B.Y(); 
	}

	inline int operator!=( const Vec2 &A, const Vec2 &B ) 
	{ 
		return A.X() != B.X() || A.Y() != B.Y(); 
	}

	inline int operator<=( const Vec2 &A, const Vec2 &B ) 
	{ 
		return A.X() <= B.X() && A.Y() <= B.Y(); 
	}

	inline int operator<( const Vec2 &A, const Vec2 &B ) 
	{ 
		return A.X() < B.X() && A.Y() < B.Y(); 
	}

	inline int operator>=( const Vec2 &A, const Vec2 &B ) 
	{ 
		return A.X() >= B.X() && A.Y() >= B.Y(); 
	}

	inline int operator>( const Vec2 &A, const Vec2 &B ) 
	{ 
		return A.X() > B.X() && A.Y() > B.Y();
	}

	//==========================================
	//===  Miscellaneous                     ===                        
	//==========================================

	inline float operator|( const Vec2 &A, const Vec2 &B )  // Inner product
	{
		return A * B;
	}

	inline Vec2 Unit( const Vec2 &A )
	{
		float c = LenSqr( A );
		if( c > 0.0 ) c = 1.0 / sqrt( c );
		return c * A;
	}

	inline Vec2 Unit( const Vec2 &A, float &len )
	{
		float c = LenSqr( A );
		if( c > 0.0 ) 
		{
			len = sqrt( c );
			return A / len;
		}
		len = 0.0;
		return A;
	}

	inline Vec2 Unit( float x, float y )
	{
		return Unit( Vec2( x, y ) );
	}

	inline double dist( const Vec2 &A, const Vec2 &B ) 
	{ 
		return Len( A - B ); 
	}

	inline float operator^( const Vec2 &A, const Vec2 &B )
	{
		return A.X() * B.Y() - A.Y() * B.X();
	}

	inline int Quadrant( const Vec2 &A )
	{
		if( A.Y() >= 0.0 ) return A.X() >= 0.0 ? 1 : 2;
		return A.X() >= 0.0 ? 4 : 3;
	}

	inline Vec2 OrthogonalTo( const Vec2 &A ) // A vector orthogonal to that given.
	{
		return Vec2( -A.Y(), A.X() );
	}

	inline Vec2 Interpolate( const Vec2 &A, const Vec2 &B, float t )
	{
		// Compute a point along the segment joining points A and B
		// according to the normalized parameter t in [0,1].
		return ( 1.0 - t ) * A + t * B;
	}

	//==========================================
	//===  Operations involving Matrices     ===                        
	//==========================================

	inline Mat2x2 Outer( const Vec2 &A, const Vec2 &B )  // Outer product.
	{
		Mat2x2 C;
		C(0,0) = A.X() * B.X();
		C(0,1) = A.X() * B.Y();
		C(1,0) = A.Y() * B.X();
		C(1,1) = A.Y() * B.Y();
		return C;
	}

	inline Vec2 operator*( const Mat2x2 &M, const Vec2 &A )
	{
		return Vec2( 
			M(0,0) * A.X() + M(0,1) * A.Y(),
			M(1,0) * A.X() + M(1,1) * A.Y()
			);
	}

	inline Mat2x2 &Mat2x2::operator*=( float scale )
	{
		m[0][0] *= scale;
		m[0][1] *= scale;
		m[1][0] *= scale;
		m[1][1] *= scale;
		return *this;
	}

	inline Mat2x2 Mat2x2::operator*( float scale ) const
	{
		return Mat2x2(
			scale * m[0][0], scale * m[0][1],       
			scale * m[1][0], scale * m[1][1]
			);
	}

	inline Mat2x2 operator*( float scale, const Mat2x2 &M )
	{
		return M * scale;
	}

	inline float Norm1( const Mat2x2 &A )
	{
		return Max( Abs(A(0,0)) + Abs(A(0,1)), Abs(A(1,0)) + Abs(A(1,1)) );
	}

	inline double det( const Mat2x2 &A )
	{
		return A(0,0) * A(1,1) - A(1,0) * A(0,1);
	}

	extern Vec2 Solve(  // Return solution x of the system Ax = b.
		const Mat2x2 &A, 
		const Vec2 &b 
		);

	//==========================================
	//===  Output routines                   ===                        
	//==========================================

	extern std::ostream &operator<<( std::ostream &out, const Vec2   & );
	extern std::ostream &operator<<( std::ostream &out, const Mat2x2 & );
};
#endif
