//===========================================================================//
// File:	angle.hh                                                            //
// Project: MUNGA           Brick: Math Library                              //
// Contents: Interface specification for angle classes                       //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 11/18/94  JMA  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#if !defined(ANGLE_HPP)
#	define ANGLE_HPP

#	if !defined(SCALAR_HPP)
#		include <scalar.hpp>
#	endif

	class Degree;
	class SinCosPair;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Radian ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	class Radian
	{
	public:
		Scalar
			angle;

		#if defined(USE_SIGNATURE)
			friend int
				Is_Signature_Bad(const volatile Radian *);
		#endif

		//
		// Constructors
		//
		Radian()
			{}
		Radian(Scalar angle)
			{this->angle = angle;}

		//
		// Assignment operators
		//
		Radian&
			operator=(Scalar angle)
				{
					Check_Pointer(this);
					this->angle = angle; return *this;
				}
		Radian&
			operator=(const Radian &radian)
				{
					Check_Pointer(this); Check(&radian);
					angle = radian.angle; return *this;
				}
		Radian&
			operator=(const Degree &degree);
		Radian&
			operator=(const SinCosPair &pair);

		//
		// Casting
		//
		operator Scalar() const
			{Check(this); return angle;}

		//
		// These comparator functions are not designed to make exact comparisons
		// of Scalaring point numbers, but rather to compare them to within some
		// specified error threshold
		//
		Logical
			operator!() const
				{Check(this); return Small_Enough(angle);}

		Logical
			operator==(const Radian &r) const
				{Check(this); Check(&r); return Close_Enough(angle,r.angle);}
		Logical
			operator==(float r) const
				{Check(this); return Close_Enough(angle,r);}

		Logical
			operator!=(const Radian &r) const
				{Check(this); Check(&r); return !Close_Enough(angle,r.angle);}
		Logical
			operator!=(float r) const
				{Check(this); return !Close_Enough(angle,r);}

		//
		// Math operators
		//
		Radian&
			Negate(Scalar r)
				{Check_Pointer(this); angle = -r; return *this;}

		Radian&
			Add(Scalar r1,Scalar r2)
				{Check_Pointer(this); angle = r1 + r2; return *this;}
		Radian&
			operator+=(Scalar r)
				{Check(this); angle += r; return *this;}

		Radian&
			Subtract(Scalar r1,Scalar r2)
				{Check_Pointer(this); angle = r1 - r2; return *this;}
		Radian&
			operator-=(Scalar r)
				{Check(this); angle -= r; return *this;}

		Radian&
			Multiply(Scalar r1,Scalar r2)
				{Check_Pointer(this); angle = r1 * r2; return *this;}
		Radian&
			operator*=(Scalar r)
				{Check(this); angle *= r; return *this;}

		Radian&
			Divide(Scalar r1,Scalar r2)
				{
					Check_Pointer(this); Verify(!Small_Enough(r2));
					angle = r1 / r2; return *this;
				}
		Radian&
			operator/=(Scalar r)
				{Check(this); Verify(!Small_Enough(r)); angle /= r; return *this;}

		//
		// Template support
		//
		Radian&
			Lerp(
				const Radian &a,
				const Radian &b,
				Scalar t
			);

		//
		// Support functions
		//
		static Scalar
			Normalize(Scalar Value);
		Radian&
			Normalize();

		friend ostream&
			operator<<(
				ostream& stream,
				const Radian &radian
			);
		Logical
			TestInstance() const;
		static Logical
			TestClass();
	};

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Degree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	class Degree
	{
	 public:
		Scalar
			angle;

		#if defined(USE_SIGNATURE)
			friend int
				Is_Signature_Bad(const volatile Degree *);
		#endif

		//
		// constructors
		//
		Degree()
			{}
		Degree(Scalar angle)
			{this->angle = angle;}

		//
		// Assignment operators
		//
		Degree&
			operator=(const Degree &degree)
				{Check(this); Check(&degree); angle = degree.angle; return *this;}
		Degree&
			operator=(Scalar angle)
				{Check(this); this->angle = angle; return *this;}
		Degree&
			operator=(const Radian &radian)
				{
					Check(this); Check(&radian);
					angle = radian.angle * DEG_PER_RAD; return *this;
				}

		//
		// Support functions
		//
		friend ostream&
			operator<<(
				ostream& stream,
				const Degree &angle
			);
		Logical
			TestInstance() const;
		static Logical
			TestClass();
	};

	inline Radian&
		Radian::operator=(const Degree& degree)
			{
				Check_Pointer(this); Check(&degree);
				angle = degree.angle * RAD_PER_DEG; return *this;
			}

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SinCosPair ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	class SinCosPair
	{
	 public:
		Scalar
			sine,
			cosine;

		#if defined(USE_SIGNATURE)
			friend int
				Is_Signature_Bad(const volatile SinCosPair *);
		#endif

		//
		// Constructors
		//
		SinCosPair()
			{}
		SinCosPair(Scalar sin, Scalar cos)
			{Check_Pointer(this); sine = sin; cosine = cos; Check(this);}

		//
		// Assignment operators
		//
		SinCosPair&
			operator=(const SinCosPair &pair)
				{
					Check_Pointer(this); Check(&pair);
					sine = pair.sine; cosine = pair.cosine; return *this;
				}
		SinCosPair&
			operator=(const Radian &radian);

		//
		// Support functions
		//
		friend ostream&
			operator<<(
				ostream& stream,
				const SinCosPair &pair
			);
		Logical
			TestInstance() const;
		static Logical
			TestClass();
	};

	inline Radian&
		Radian::operator=(const SinCosPair& pair)
			{
				Check_Pointer(this); Check(&pair);
				angle = Arctan(pair.sine,pair.cosine); return *this;
			}

#endif

