///Current version
///ID:          $Id: Projection.cs,v 1.1.1.1 2006/11/14 02:56:41 gupchup Exp $
///Revision:    $Revision: 1.1.1.1 $
///Date:        $Date: 2006/11/14 02:56:41 $

using System;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Spherical.Visualizer
{
	/// <summary>
	/// Interface definition for different projections.
	/// </summary>
	internal interface IProjection
	{
		PointF EqToScreen(double ra_, double dec_, float size_);
		PointF EqToScreen(PointEq e_, float size_);
		PointEq ScreenToEq(PointF p_);
	}

	/// <summary>
	/// CARTProjection
	/// Gives us the definition of the projection to the screen
	/// using a cartesian projection, centered on (ra=180, dec=0).
	/// </summary>
	internal class CARTProjection: IProjection
	{
		public static string Revision
		{
			get 
			{
				return "$Revision: 1.1.1.1 $";
			}
		}
		private Int32  width, height;					// screen size
		private PointF cOffset;							// reference point offset
		private double scale;							// image scale
		private double[] n, w, u;						// normal, west, up vectors
		/// <summary>
		/// InitProjection. Initialize the transformation for the projection
		/// to be used on the screen.
		/// </summary>	
		public CARTProjection(double ra_, double dec_, double ppd_, int width_, int height_)
		{
			ra_  = 180.0;
			dec_ = 0.0;
			n = V3.Normal(ra_,dec_);
			w = V3.West(ra_,dec_);
			u = V3.North(ra_,dec_);

			scale		= ppd_/V3.D2R;
			width		= width_;
			height		= height_;
			cOffset		= new PointF((float)(width/2.0),(float)(height/2.0));
		}		

		//------------------------------
		// Coordinate transformations
		//------------------------------
		
		/// <summary>
		/// EqToScreen. Maps an (ra,dec) coordinate to the Screen.
		/// This is the basic coordinate transformation function
		///  </summary>
		/// <param name="ra_">Right ascension in degrees, double</param>
		/// <param name="dec_">Declination in degrees, double</param>
		/// <param name="size_">Symbol size in pixels, float</param>
		public PointF EqToScreen(double ra_, double dec_, float size_) 
		{

			double [] r = V3.Normal(ra_,dec_);
			double gn=Math.Cos(dec_*V3.D2R);
			double gw;
			if (gn==0) 
			{
				gw = 0;
			} 
			else 
			{
				gw = (ra_/180.0-1)/Math.Cos(dec_*V3.D2R);
			}
			double gu= (dec_/90.0);
			PointF q = new PointF(cOffset.X-(float)(scale*gw),cOffset.Y-(float)(scale*gu));
			return q;
/*  old code from TAN
			
			double gn=0;
			double gw=0;
			double gu=0;
			for (int i=0;i<3;i++) 
			{
				gn += r[i]*n[i];
				gw += r[i]*w[i];
				gu += r[i]*u[i];
			}
			PointF q = new PointF(cOffset.X-(float)(scale*gw),cOffset.Y-(float)(scale*gu));
			return q;
*/
		}

		public PointF EqToScreen(PointEq e, float size_) 
		{
			return EqToScreen(e.ra, e.dec,size_);
		}


		/// <summary>
		/// ScreenToEq. Maps a screen coordinate to an (ra,dec).
		/// This is the inverse coordinate transformation function
		///  </summary>
		/// <param name="p">The screen coordinate of the point</param>
		public PointEq ScreenToEq(PointF p) 
		{
			double[] r = new Double[3];

			//----------------------------------------
			// first subtract the offset, and rescale
			//----------------------------------------
			PointF q = new PointF((float)((cOffset.X-p.X)/scale), (float)((cOffset.Y-p.Y)/scale));

			//------------------------------------------------
			// now these are the dot products with w and u
			// compute the dot product with the normal vector
			//------------------------------------------------
			double qdec = 90*q.Y;
			double qra  = 180 + 180* q.X * Math.Cos(qdec*V3.D2R);
/*
			gn = Math.Sqrt(gn);
			for (int i=0;i<3;i++) 
			{
				r[i] = gn*n[i]+q.X*w[i]+q.Y*u[i];
			}
			PointEq g = V3.ToEq(r);
*/
			PointEq g = new PointEq(qra,qdec);
			return g;
		}
	}

	
	/// <summary>
	/// TANProjection
	/// Gives us the definition of the projection to the screen
	/// using a tangent plane projection, centered on (ra,dec).
	/// </summary>
	internal class TANProjection: IProjection
	{
		public static string Revision
		{
			get 
			{
				return "$Revision: 1.1.1.1 $";
			}
		}
		private Int32  width, height;					// screen size
		private PointF cOffset;							// reference point offset
		private double scale;							// image scale
		private double[] n, w, u;						// normal, west, up vectors
		/// <summary>
		/// InitProjection. Initialize the transformation for the projection
		/// to be used on the screen.
		/// </summary>	
		public TANProjection(double ra_, double dec_, double ppd_, int width_, int height_)
		{
			n = V3.Normal(ra_,dec_);
			w = V3.West(ra_,dec_);
			u = V3.North(ra_,dec_);

			scale		= ppd_/V3.D2R;
			width		= width_;
			height		= height_;
			cOffset		= new PointF((float)(width/2.0),(float)(height/2.0));
		}		

		//------------------------------
		// Coordinate transformations
		//------------------------------
		
		/// <summary>
		/// EqToScreen. Maps an (ra,dec) coordinate to the Screen.
		/// This is the basic coordinate transformation function
		///  </summary>
		/// <param name="ra_">Right ascension in degrees, double</param>
		/// <param name="dec_">Declination in degrees, double</param>
		/// <param name="size_">Symbol size in pixels, float</param>
		public PointF EqToScreen(double ra_, double dec_, float size_) 
		{
			double [] r = V3.Normal(ra_,dec_);
			double gn=0;
			double gw=0;
			double gu=0;
			for (int i=0;i<3;i++) 
			{
				gn += r[i]*n[i];
				gw += r[i]*w[i];
				gu += r[i]*u[i];
			}
			PointF q = new PointF(cOffset.X-(float)(scale*gw),cOffset.Y-(float)(scale*gu));
			return q;
		}

		public PointF EqToScreen(PointEq e, float size_) 
		{
			return EqToScreen(e.ra, e.dec,size_);
		}


		/// <summary>
		/// ScreenToEq. Maps a screen coordinate to an (ra,dec).
		/// This is the inverse coordinate transformation function
		///  </summary>
		/// <param name="p">The screen coordinate of the point</param>
		public PointEq ScreenToEq(PointF p) 
		{
			double[] r = new Double[3];

			//----------------------------------------
			// first subtract the offset, and rescale
			//----------------------------------------
			PointF q = new PointF((float)((cOffset.X-p.X)/scale), (float)((cOffset.Y-p.Y)/scale));

			//------------------------------------------------
			// now these are the dot products with w and u
			// compute the dot product with the normal vector
			//------------------------------------------------
			double gn = 1.0-q.X*q.X-q.Y*q.Y;
			gn = Math.Sqrt(gn);
			for (int i=0;i<3;i++) 
			{
				r[i] = gn*n[i]+q.X*w[i]+q.Y*u[i];
			}
			PointEq g = V3.ToEq(r);
			return g;
		}
	}

	/// <summary>
	/// STRProjection
	/// Gives us the definition of the projection to the screen
	/// using a stereographic projection, centered on (ra,dec).
	/// </summary>
	internal class STRProjection: IProjection
	{
		public static string Revision
		{
			get 
			{
				return "$Revision: 1.1.1.1 $";
			}
		}
		private Int32  width, height;					// screen size
		private PointF cOffset;							// reference point offset
		private double scale;							// image scale
		private double[] n, w, u;
		/// <summary>
		/// InitProjection. Initialize the transformation for the projection
		/// to be used on the screen.
		/// </summary>	
		public STRProjection(double ra_, double dec_, double ppd_, int width_, int height_)
		{
			n = V3.Normal(ra_,dec_);
			w = V3.West(ra_,dec_);
			u = V3.North(ra_,dec_);

			scale		= ppd_/V3.D2R;
			width		= width_;
			height		= height_;
			cOffset		= new PointF((float)(width/2.0),(float)(height/2.0));
		}		

		//-----------------------------
		// Coordinate transformations
		//-----------------------------
		
		/// <summary>
		/// EqToScreen. Maps an (ra,dec) coordinate to the Screen.
		/// This is the basic coordinate transformation function
		///  </summary>
		/// <param name="ra_">Right ascension in degrees, double</param>
		/// <param name="dec_">Declination in degrees, double</param>
		/// <param name="size_">Symbol size in pixels, float</param>
		public PointF EqToScreen(double ra_, double dec_, float size_) 
		{
			double [] r = V3.Normal(ra_,dec_);
			double gn=0;
			double gw=0;
			double gu=0;
			for (int i=0;i<3;i++) 
			{
				gn += r[i]*n[i];
				gw += r[i]*w[i];
				gu += r[i]*u[i];
			}
			double a = 2.0/(1+gn);
			PointF q = new PointF(cOffset.X-(float)(scale*a*gw),cOffset.Y-(float)(scale*a*gu));
			return q;
		}

		public PointF EqToScreen(PointEq e, float size_) 
		{
			return EqToScreen(e.ra, e.dec,size_);
		}


		public PointEq ScreenToEq(PointF p) 
		{
			double[] r = new Double[3];

			//----------------------------------------
			// first subtract the offset, and rescale
			//----------------------------------------
			PointF q = new PointF((float)((cOffset.X-p.X)/scale), (float)((cOffset.Y-p.Y)/scale));

			//------------------------------------------------
			// now these are the dot products with w and u
			// compute the dot product with the normal vector
			//------------------------------------------------
			double rho	 = 0.25*(q.X*q.X + q.Y*q.Y);
			for (int i=0;i<3;i++) 
			{
				r[i] = ((1.0-rho)*n[i]+q.X*w[i]+q.Y*u[i])/(1.0+rho);
			}
			PointEq g = V3.ToEq(r);
			return g;
		}
	}
}

/* Revision History

        $Log: Projection.cs,v $
        Revision 1.1.1.1  2006/11/14 02:56:41  gupchup
        no message

        Revision 1.2  2006/02/28 16:44:00  budavari
        new lib

        Revision 1.1  2006/02/03 21:16:42  budavari
        Added module for visualization - based on Geom2

        Revision 1.2  2003/04/10 21:54:32  szalay
         projection interface added
        
		
*/
