/*
 * NPlot - A charting library for .NET
 * 
 * Transform2D.cs
 * Copyright (C) 2003-2006 Matt Howlett and others.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of NPlot nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Drawing;

namespace NPlot
{

	/// <summary>
	/// Encapsulates functionality for transforming world to physical coordinates optimally.
	/// </summary>
	/// <remarks>The existence of the whole ITransform2D thing might need revising. Not convinced it's the best way.</remarks>
	public class Transform2D
	{

		/// <summary>
		/// Constructs the optimal ITransform2D object for the supplied x and y axes.
		/// </summary>
		/// <param name="xAxis">The xAxis to use for the world to physical transform.</param>
		/// <param name="yAxis">The yAxis to use for the world to physical transform.</param>
		/// <returns>An ITransform2D derived object for converting from world to physical coordinates.</returns>
		public static ITransform2D GetTransformer( PhysicalAxis xAxis, PhysicalAxis yAxis )
		{
			ITransform2D ret = null;

//			if (xAxis.Axis.IsLinear && yAxis.Axis.IsLinear && !xAxis.Axis.Reversed && !yAxis.Axis.Reversed)
//				ret = new FastTransform2D( xAxis, yAxis );
//			else 
//				ret = new DefaultTransform2D( xAxis, yAxis );

			ret = new DefaultTransform2D( xAxis, yAxis );

			return ret;
		}


		/// <summary>
		/// This class does world -> physical transforms for the general case
		/// </summary>
		public class DefaultTransform2D : ITransform2D
		{
			private PhysicalAxis xAxis_;
			private PhysicalAxis yAxis_;

			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="xAxis">The x-axis to use for transforms</param>
			/// <param name="yAxis">The y-axis to use for transforms</param>
			public DefaultTransform2D( PhysicalAxis xAxis, PhysicalAxis yAxis )
			{
				xAxis_ = xAxis;
				yAxis_ = yAxis;
			}


			/// <summary>
			/// Transforms the given world point to physical coordinates
			/// </summary>
			/// <param name="x">x coordinate of world point to transform.</param>
			/// <param name="y">y coordinate of world point to transform.</param>
			/// <returns>the corresponding physical point.</returns>
			public PointF Transform( double x, double y )
			{
				return new PointF(
					xAxis_.WorldToPhysical( x, false ).X,
					yAxis_.WorldToPhysical( y, false ).Y );
			}


			/// <summary>
			/// Transforms the given world point to physical coordinates
			/// </summary>
			/// <param name="worldPoint">the world point to transform</param>
			/// <returns>the corresponding physical point</returns>
			public PointF Transform( PointD worldPoint )
			{
				return new PointF( 
					xAxis_.WorldToPhysical( worldPoint.X, false ).X,
					yAxis_.WorldToPhysical( worldPoint.Y, false ).Y );
			}

		}
	



		/// <summary>
		/// This class does highly efficient world->physical and physical->world transforms
		/// for linear axes. 
		/// </summary>
		public class FastTransform2D : ITransform2D
		{

			private PageAlignedPhysicalAxis xAxis_;
			private PageAlignedPhysicalAxis yAxis_;


			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="xAxis">The x-axis to use for transforms</param>
			/// <param name="yAxis">The y-axis to use for transforms</param>
			public FastTransform2D( PhysicalAxis xAxis, PhysicalAxis yAxis )
			{
				xAxis_ = new PageAlignedPhysicalAxis( xAxis );
				yAxis_ = new PageAlignedPhysicalAxis( yAxis );
			}


			/// <summary>
			/// Transforms the given world point to physical coordinates
			/// </summary>
			/// <param name="x">x coordinate of world point to transform.</param>
			/// <param name="y">y coordinate of world point to transform.</param>
			/// <returns>the corresponding physical point.</returns>
			public PointF Transform( double x, double y )
			{
				return new PointF(
					xAxis_.WorldToPhysicalClipped( x ),
					yAxis_.WorldToPhysicalClipped( y ) );
			}


			/// <summary>
			/// Transforms the given world point to physical coordinates
			/// </summary>
			/// <param name="worldPoint">the world point to transform</param>
			/// <returns>the corresponding physical point</returns>
			public PointF Transform( PointD worldPoint )
			{
				return new PointF( 
					xAxis_.WorldToPhysical( worldPoint.X ),
					yAxis_.WorldToPhysical( worldPoint.Y ) );
			}

		}


	}
}
