//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright © 2004  John Champion
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================
#region Using directives
using System;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.IO;
#endregion
namespace DrawGraph
{
	/// 
	/// An abstract base class that defines basic functionality for handling a pane.  This class is the
	/// parent class for  and .
	/// 
	/// 
	/// John Champion
	///  $Revision: 3.30 $ $Date: 2007/05/18 13:28:17 $ 
	abstract public class PaneBase : ICloneable
	{
	#region Fields
		/// 
		/// The rectangle that defines the full area into which the pane is rendered.  Units are pixels.
		/// Use the public property  to access this value.
		/// 
		protected RectangleF	_rect;
		
		/// Private field that holds the main title of the pane.  Use the
		/// public property  to access this value.
		/// 
		protected GapLabel _title;
		
		/// Private field instance of the  class.  Use the
		/// public property  to access this class.
		protected Legend _legend;
		/// 
		/// Private field that stores the user-defined tag for this .  This tag
		/// can be any user-defined value.  If it is a  type, it can be used as
		/// a parameter to the  method.  Use the public property
		///  to access this value.
		/// 
		protected object _tag;
		/// 
		/// private field to store the margin values for this . Use the
		/// public property  to access this property.
		/// 
		internal Margin _margin;
		/// Private field that determines whether or not the fonts, tics, gaps, etc.
		/// will be scaled according to the actual graph size.  true for font and feature scaling
		/// with graph size, false for fixed font sizes (scaleFactor = 1.0 constant).
		/// Use the public property  to access this value. 
		/// 
		/// 
		protected bool _isFontsScaled;
		/// 
		/// Private field that controls whether or not pen widths are scaled according to the
		/// size of the graph.  This value is only applicable if 
		/// is true.  If  is false, then no scaling will be done,
		/// regardless of the value of .
		/// 
		/// true to scale the pen widths according to the size of the graph,
		/// false otherwise.
		/// 
		/// 
		protected bool _isPenWidthScaled;
		/// 
		/// Private field that stores the  data for the
		///  background.  Use the public property  to
		/// access this value.
		/// 
		protected Fill	_fill;
		/// 
		/// Private field that stores the  data for the
		///  border.  Use the public property  to
		/// access this value.
		/// 
		protected Border _border;
		/// Private field instance of the  class.  Use the
		/// public property  to access this class.
		protected GraphObjList _graphObjList;
		/// Private field that determines the base size of the pane, in inches.
		/// Fonts, tics, gaps, etc. are scaled according to this base size.
		/// Use the public property  to access this value. 
		/// 
		/// 
		protected float _baseDimension;
		/// 
		/// private field that stores the gap between the bottom of the pane title and the
		/// client area of the pane.  This is expressed as a fraction of the title character height.
		/// 
		protected float _titleGap;
	#endregion
	#region Defaults
		/// 
		/// A simple struct that defines the default property values for the  class.
		/// 
		public struct Default
		{
			// Default GraphPane properties
			/// 
			/// The default display mode for the title at the top of the pane
			/// (  property).  true to
			/// display a title, false otherwise.
			/// 
			public static bool IsShowTitle = true;
			
			/// 
			/// The default font family for the title
			/// ( property).
			/// 
			public static string FontFamily = "Arial";
			/// 
			/// The default font size (points) for the
			///  ( property).
			/// 
			public static float FontSize = 16;
			/// 
			/// The default font color for the
			/// 
			/// ( property).
			/// 
			public static Color FontColor = Color.Black;
			/// 
			/// The default font bold mode for the
			/// 
			/// ( property). true
			/// for a bold typeface, false otherwise.
			/// 
			public static bool FontBold = true;
			/// 
			/// The default font italic mode for the
			/// 
			/// ( property). true
			/// for an italic typeface, false otherwise.
			/// 
			public static bool FontItalic = false;
			/// 
			/// The default font underline mode for the
			/// 
			/// ( property). true
			/// for an underlined typeface, false otherwise.
			/// 
			public static bool FontUnderline = false;
			
			/// 
			/// The default border mode for the .
			/// ( property). true
			/// to draw a border around the ,
			/// false otherwise.
			/// 
			public static bool IsBorderVisible = true;
			/// 
			/// The default color for the  border.
			/// ( property). 
			/// 
			public static Color BorderColor = Color.Black;
			/// 
			/// The default color for the  background.
			/// ( property). 
			/// 
			public static Color FillColor = Color.White;
			/// 
			/// The default pen width for the  border.
			/// ( property).  Units are in points (1/72 inch).
			/// 
			public static float BorderPenWidth = 1;
			/// 
			/// The default dimension of the , which
			/// defines a normal sized plot.  This dimension is used to scale the
			/// fonts, symbols, etc. according to the actual size of the
			/// .
			/// 
			/// 
			public static float BaseDimension = 8.0F;
			
			/// 
			/// The default setting for the  option.
			/// true to have all pen widths scaled according to ,
			/// false otherwise.
			/// 
			/// 
			public static bool IsPenWidthScaled = false;
			/// 
			/// The default setting for the  option.
			/// true to have all fonts scaled according to ,
			/// false otherwise.
			/// 
			/// 
			public static bool IsFontsScaled = true;
			/// 
			/// The default value for the  property, expressed as
			/// a fraction of the scaled  character height.
			/// 
			public static float TitleGap = 0.5f;
		}
	#endregion
	#region Properties
		/// 
		/// The rectangle that defines the full area into which all graphics
		/// will be rendered.
		/// 
		/// Note that this rectangle has x, y, width, and height.  Most of the
		/// GDI+ graphic primitive actually draw one pixel beyond those dimensions.  For
		/// example, for a rectangle of ( X=0, Y=0, Width=100, Height=100 ), GDI+ would
		/// draw into pixels 0 through 100, which is actually 101 pixels.  For the
		/// ZedGraph Rect, a Width of 100 pixels means that pixels 0 through 99 are used
		/// Units are pixels.
		/// 
		public RectangleF Rect
		{
			get { return _rect; }
			set { _rect = value; }
		}
		/// 
		/// Accesses the  for this 
		/// 
		/// A reference to a  object
		public Legend Legend
		{
			get { return _legend; }
		}
		/// 
		/// Gets the  instance that contains the text and attributes of the title.
		/// This text can be multiple lines separated by newline characters ('\n').
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		/// 
		public Label Title
		{
			get { return _title; }
		}
		/// 
		/// Gets or sets the user-defined tag for this .  This tag
		/// can be any user-defined value.  If it is a  type, it can be used as
		/// a parameter to the  method.
		/// 
		/// 
		/// Note that, if you are going to Serialize ZedGraph data, then any type
		/// that you store in  must be a serializable type (or
		/// it will cause an exception).
		/// 
		public object Tag
		{
			get { return _tag; }
			set { _tag = value; }
		}
		/// 
		/// Gets or sets the  class for drawing the border
		/// border around the 
		/// 
		/// 
		/// 
		public Border Border
		{
			get { return _border; }
			set { _border = value; }
		}
		/// 
		/// Gets or sets the  data for the
		/// filling the background of the .
		/// 
		public Fill	Fill
		{
			get { return _fill; }
			set { _fill = value; }
		}
		/// 
		/// Gets or sets the list of  items for this 
		/// 
		/// A reference to a  collection object
		public GraphObjList GraphObjList
		{
			get { return _graphObjList; }
			set { _graphObjList = value; }
		}
		/// 
		/// Gets or sets the  instance that controls the space between
		/// the edge of the  and the rendered content of the graph.
		/// 
		public Margin Margin
		{
			get { return _margin; }
			set { _margin = value; }
		}
		/// 
		/// BaseDimension is a double precision value that sets "normal" pane size on
		/// which all the settings are based.  The BaseDimension is in inches.  For
		/// example, if the BaseDimension is 8.0 inches and the
		///  size is 14 points.  Then the pane title font
		/// will be 14 points high when the  is approximately 8.0
		/// inches wide.  If the Rect is 4.0 inches wide, the pane title font will be
		/// 7 points high.  Most features of the graph are scaled in this manner.
		/// 
		/// The base dimension reference for the , in inches
		/// 
		/// 
		/// 
		public float BaseDimension
		{
			get { return _baseDimension; }
			set { _baseDimension = value; }
		}
		/// 
		/// Gets or sets the gap between the bottom of the pane title and the
		/// client area of the pane.  This is expressed as a fraction of the scaled
		///  character height.
		/// 
		public float TitleGap
		{
			get { return _titleGap; }
			set { _titleGap = value; }
		}
		/// 
		/// Determines if the font sizes, tic sizes, gap sizes, etc. will be scaled according to
		/// the size of the  and the .  If this
		/// value is set to false, then the font sizes and tic sizes will always be exactly as
		/// specified, without any scaling.
		/// 
		/// True to have the fonts and tics scaled, false to have them constant
		/// 
		public bool IsFontsScaled
		{
			get { return _isFontsScaled; }
			set { _isFontsScaled = value; }
		}
		/// 
		/// Gets or sets the property that controls whether or not pen widths are scaled for this
		/// .
		/// 
		/// This value is only applicable if 
		/// is true.  If  is false, then no scaling will be done,
		/// regardless of the value of .  Note that scaling the pen
		/// widths can cause "artifacts" to appear at typical screen resolutions.  This occurs
		/// because of roundoff differences; in some cases the pen width may round to 1 pixel wide
		/// and in another it may round to 2 pixels wide.  The result is typically undesirable.
		/// Therefore, this option defaults to false.  This option is primarily useful for high
		/// resolution output, such as printer output or high resolution bitmaps (from
		/// ) where it is desirable to have the pen width
		/// be consistent with the screen image.
		/// 
		/// true to scale the pen widths according to the size of the graph,
		/// false otherwise.
		/// 
		/// 
		public bool IsPenWidthScaled
		{
			get { return _isPenWidthScaled; }
			set { _isPenWidthScaled = value; }
		}
	#endregion
	
	#region Constructors
		/// 
		/// Default constructor for the  class.  Leaves the  empty.
		/// 
		public PaneBase() : this( "", new RectangleF( 0, 0, 0, 0 ) )
		{
		}
		
		/// 
		/// Default constructor for the  class.  Specifies the  of
		/// the , and the size of the .
		/// 
		public PaneBase( string title, RectangleF paneRect )
		{
			_rect = paneRect;
			_legend = new Legend();
				
			_baseDimension = Default.BaseDimension;
			_margin = new Margin();
			_titleGap = Default.TitleGap;
			_isFontsScaled = Default.IsFontsScaled;
			_isPenWidthScaled = Default.IsPenWidthScaled;
			_fill = new Fill( Default.FillColor );
			_border = new Border( Default.IsBorderVisible, Default.BorderColor,
				Default.BorderPenWidth );
			_title = new GapLabel( title, Default.FontFamily,
				Default.FontSize, Default.FontColor, Default.FontBold,
				Default.FontItalic, Default.FontUnderline );
			_title._fontSpec.Fill.IsVisible = false;
			_title._fontSpec.Border.IsVisible = false;
			_graphObjList = new GraphObjList();
			
			_tag = null;
		}
		/// 
		/// The Copy Constructor
		/// 
		/// The  object from which to copy
		public PaneBase( PaneBase rhs )
		{
			// copy over all the value types
			_isFontsScaled = rhs._isFontsScaled;
			_isPenWidthScaled = rhs._isPenWidthScaled;
			_titleGap = rhs._titleGap;
			_baseDimension = rhs._baseDimension;
			_margin = rhs._margin.Clone();
			_rect = rhs._rect;
			// Copy the reference types by cloning
			_fill = rhs._fill.Clone();
			_border = rhs._border.Clone();
			_title = rhs._title.Clone();
			_legend = rhs.Legend.Clone();
			_title = rhs._title.Clone();
			_graphObjList = rhs._graphObjList.Clone();
			
			if ( rhs._tag is ICloneable )
				_tag = ((ICloneable) rhs._tag).Clone();
			else
				_tag = rhs._tag;
		}
		//abstract public object ShallowClone();
		/// 
		/// Implement the  interface in a typesafe manner by just
		/// calling the typed version of Clone
		/// 
		/// 
		/// Note that this method must be called with an explicit cast to ICloneable, and
		/// that it is inherently virtual.  For example:
		/// 
		/// ParentClass foo = new ChildClass();
		/// ChildClass bar = (ChildClass) ((ICloneable)foo).Clone();
		/// 
		/// Assume that ChildClass is inherited from ParentClass.  Even though foo is declared with
		/// ParentClass, it is actually an instance of ChildClass.  Calling the ICloneable implementation
		/// of Clone() on foo actually calls ChildClass.Clone() as if it were a virtual function.
		/// 
		/// A deep copy of this object
		object ICloneable.Clone()
		{
			throw new NotImplementedException( "Can't clone an abstract base type -- child types must implement ICloneable" );
			//return new PaneBase( this );
		}
		/// 
		/// Create a shallow, memberwise copy of this class.
		/// 
		/// 
		/// Note that this method uses MemberWiseClone, which will copy all
		/// members (shallow) including those of classes derived from this class.
		/// a new copy of the class
		public PaneBase ShallowClone()
		{
			// return a shallow copy
			return this.MemberwiseClone() as PaneBase;
		}
	#endregion
	#region Serialization
		/// 
		/// Current schema value that defines the version of the serialized file
		/// 
		// schema changed to 2 when Label Class added
		public const int schema = 10;
		/// 
		/// Constructor for deserializing objects
		/// 
		/// A  instance that defines the serialized data
		/// 
		/// A  instance that contains the serialized data
		/// 
		protected PaneBase( SerializationInfo info, StreamingContext context )
		{
			// The schema value is just a file version parameter.  You can use it to make future versions
			// backwards compatible as new member variables are added to classes
			int sch = info.GetInt32( "schema" );
			_rect = (RectangleF) info.GetValue( "rect", typeof(RectangleF) );
			_legend = (Legend) info.GetValue( "legend", typeof(Legend) );
			_title = (GapLabel) info.GetValue( "title", typeof(GapLabel) );
			//this.isShowTitle = info.GetBoolean( "isShowTitle" );
			_isFontsScaled = info.GetBoolean( "isFontsScaled" );
			_isPenWidthScaled = info.GetBoolean( "isPenWidthScaled" );
			//this.fontSpec = (FontSpec) info.GetValue( "fontSpec" , typeof(FontSpec) );
			_titleGap = info.GetSingle( "titleGap" );
			_fill = (Fill) info.GetValue( "fill", typeof(Fill) );
			_border = (Border) info.GetValue( "border", typeof(Border) );
			_baseDimension = info.GetSingle( "baseDimension" );
			_margin = (Margin)info.GetValue( "margin", typeof( Margin ) );
			_graphObjList = (GraphObjList) info.GetValue( "graphObjList", typeof(GraphObjList) );
			_tag = info.GetValue( "tag", typeof(object) );
		}
		/// 
		/// Populates a  instance with the data needed to serialize the target object
		/// 
		/// A  instance that defines the serialized data
		/// A  instance that contains the serialized data
		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public virtual void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			info.AddValue( "schema", schema );
			info.AddValue( "rect", _rect );
			info.AddValue( "legend", _legend );
			info.AddValue( "title", _title );
			//info.AddValue( "isShowTitle", isShowTitle );
			info.AddValue( "isFontsScaled", _isFontsScaled );
			info.AddValue( "isPenWidthScaled", _isPenWidthScaled );
			info.AddValue( "titleGap", _titleGap );
			//info.AddValue( "fontSpec", fontSpec );
			info.AddValue( "fill", _fill );
			info.AddValue( "border", _border );
			info.AddValue( "baseDimension", _baseDimension );
			info.AddValue( "margin", _margin );
			info.AddValue( "graphObjList", _graphObjList );
			info.AddValue( "tag", _tag );
		}
	#endregion
	#region Methods
		/// 
		/// Do all rendering associated with this  to the specified
		///  device.  This abstract method is implemented by the child
		/// classes.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		public virtual void Draw( Graphics g  )
		{
			if ( _rect.Width <= 1 || _rect.Height <= 1 )
				return;
			// calculate scaleFactor on "normal" pane size (BaseDimension)
			float scaleFactor = this.CalcScaleFactor();
			// Fill the pane background and draw a border around it			
			DrawPaneFrame( g, scaleFactor );
			// Clip everything to the rect
			g.SetClip( _rect );
			// Draw the GraphItems that are behind everything
			_graphObjList.Draw( g, this, scaleFactor, ZOrder.H_BehindAll );
			// Draw the Pane Title
			DrawTitle( g, scaleFactor );
			// Draw the Legend
			//this.Legend.Draw( g, this, scaleFactor );
			// Reset the clipping
			g.ResetClip();
		}
		/// 
		/// Calculate the client area rectangle based on the .
		/// 
		/// The client rectangle is the actual area available for 
		/// or  items after taking out space for the margins and the title.
		/// This method does not take out the area required for the .
		/// To do so, you must separately call .
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// The scaling factor for the features of the graph based on the .  This
		/// scaling factor is calculated by the  method.  The scale factor
		/// represents a linear multiple to be applied to font sizes, symbol sizes, etc.
		/// 
		/// The calculated chart rect, in pixel coordinates.
		public RectangleF CalcClientRect( Graphics g, float scaleFactor )
		{
			// get scaled values for the paneGap and character height
			//float scaledOuterGap = (float) ( Default.OuterPaneGap * scaleFactor );
			float charHeight = _title._fontSpec.GetHeight( scaleFactor );
			// chart rect starts out at the full pane rect.  It gets reduced to make room for the legend,
			// scales, titles, etc.
			RectangleF innerRect = new RectangleF(
							_rect.Left + _margin.Left * scaleFactor,
							_rect.Top + _margin.Top * scaleFactor,
							_rect.Width - scaleFactor * ( _margin.Left + _margin.Right ),
							_rect.Height - scaleFactor * ( _margin.Top + _margin.Bottom ) );
			// Leave room for the title
			if ( _title._isVisible && _title._text != string.Empty )
			{
				SizeF titleSize = _title._fontSpec.BoundingBox( g, _title._text, scaleFactor );
				// Leave room for the title height, plus a line spacing of charHeight * _titleGap
				innerRect.Y += titleSize.Height + charHeight * _titleGap;
				innerRect.Height -= titleSize.Height + charHeight * _titleGap;
			}
			// Calculate the legend rect, and back it out of the current ChartRect
			//this.legend.CalcRect( g, this, scaleFactor, ref innerRect );
			return innerRect;
		}
		/// 
		/// Draw the border _border around the  area.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// The scaling factor for the features of the graph based on the .  This
		/// scaling factor is calculated by the  method.  The scale factor
		/// represents a linear multiple to be applied to font sizes, symbol sizes, etc.
		/// 		
		public void DrawPaneFrame( Graphics g, float scaleFactor )
		{
			// Erase the pane background, filling it with the specified brush
			_fill.Draw( g, _rect );
			// Reduce the rect width and height by 1 pixel so that for a rect of
			// new RectangleF( 0, 0, 100, 100 ), which should be 100 pixels wide, we cover
			// from 0 through 99.  The draw routines normally cover from 0 through 100, which is
			// actually 101 pixels wide.
			RectangleF rect = new RectangleF( _rect.X, _rect.Y, _rect.Width - 1, _rect.Height - 1 );
			_border.Draw( g, this, scaleFactor, rect );
		}
		/// 
		/// Draw the  on the graph, centered at the top of the pane.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// 
		/// The scaling factor for the features of the graph based on the .  This
		/// scaling factor is calculated by the  method.  The scale factor
		/// represents a linear multiple to be applied to font sizes, symbol sizes, etc.
		/// 		
		public void DrawTitle( Graphics g, float scaleFactor )
		{	
			// only draw the title if it's required
			if ( _title._isVisible )
			{
				SizeF size = _title._fontSpec.BoundingBox( g, _title._text, scaleFactor );
				
				// use the internal fontSpec class to draw the text using user-specified and/or
				// default attributes.
				_title._fontSpec.Draw( g, this, _title._text,
					( _rect.Left + _rect.Right ) / 2,
					_rect.Top + _margin.Top * (float) scaleFactor + size.Height / 2.0F,
					AlignH.Center, AlignV.Center, scaleFactor );
			}
		}
		/// 
		/// Change the size of the .  Override this method to handle resizing the contents
		/// as required.
		/// 
		/// 
		/// A graphic device object to be drawn into.  This is normally e.Graphics from the
		/// PaintEventArgs argument to the Paint() method.
		/// 
		/// The new size for the .
		public virtual void ReSize( Graphics g, RectangleF rect )
		{
			_rect = rect;
		}
		/// 
		/// Calculate the scaling factor based on the ratio of the current  dimensions and
		/// the .
		/// 
		/// This scaling factor is used to proportionally scale the
		/// features of the  so that small graphs don't have huge fonts, and vice versa.
		/// The scale factor represents a linear multiple to be applied to font sizes, symbol sizes, tic sizes,
		/// gap sizes, pen widths, etc.  The units of the scale factor are "World Pixels" per "Standard Point".
		/// If any object size, in points, is multiplied by this scale factor, the result is the size, in pixels,
		/// that the object should be drawn using the standard GDI+ drawing instructions.  A "Standard Point"
		/// is a dimension based on points (1/72nd inch) assuming that the  size
		/// matches the .
		/// Note that "World Pixels" will still be transformed by the GDI+ transform matrices to result
		/// in "Output Device Pixels", but "World Pixels" are the reference basis for the drawing commands.
		/// 
		/// 
		/// A  value representing the scaling factor to use for the rendering calculations.
		/// 
		/// 
		public float CalcScaleFactor()
		{
			float scaleFactor; //, xInch, yInch;
			const float ASPECTLIMIT = 1.5F;
			
			// if font scaling is turned off, then always return a 1.0 scale factor
			if ( !_isFontsScaled )
				return 1.0f;
			// Assume the standard width (BaseDimension) is 8.0 inches
			// Therefore, if the rect is 8.0 inches wide, then the fonts will be scaled at 1.0
			// if the rect is 4.0 inches wide, the fonts will be half-sized.
			// if the rect is 16.0 inches wide, the fonts will be double-sized.
		
			// Scale the size depending on the client area width in linear fashion
			if ( _rect.Height <= 0 )
				return 1.0F;
			float length = _rect.Width;
			float aspect = _rect.Width / _rect.Height;
			if ( aspect > ASPECTLIMIT )
				length = _rect.Height * ASPECTLIMIT;
			if ( aspect < 1.0F / ASPECTLIMIT )
				length = _rect.Width * ASPECTLIMIT;
			scaleFactor = length / ( _baseDimension * 72F );
			// Don't let the scaleFactor get ridiculous
			if ( scaleFactor < 0.1F )
				scaleFactor = 0.1F;
						
			return scaleFactor;
		}
		/// 
		/// Calculate the scaled pen width, taking into account the scaleFactor and the
		/// setting of the  property of the pane.
		/// 
		/// The pen width, in points (1/72 inch)
		/// 
		/// The scaling factor for the features of the graph based on the .  This
		/// scaling factor is calculated by the  method.  The scale factor
		/// represents a linear multiple to be applied to font sizes, symbol sizes, etc.
		/// 
		/// The scaled pen width, in world pixels
		public float ScaledPenWidth( float penWidth, float scaleFactor )
		{
			if ( _isPenWidthScaled )
				return (float)( penWidth * scaleFactor );
			else
				return penWidth;
		}
		/// 
		/// Build a  object containing the graphical rendering of
		/// all the  objects in this list.
		/// 
		/// A  object rendered with the current graph.
		/// 
		/// 
		/// 
		public Bitmap GetImage()
		{
			Bitmap bitmap = new Bitmap( (int)_rect.Width, (int)_rect.Height );
			using ( Graphics bitmapGraphics = Graphics.FromImage( bitmap ) )
			{
				bitmapGraphics.TranslateTransform( -_rect.Left, -_rect.Top );
				this.Draw( bitmapGraphics );
			}
			return bitmap;
		}
		/// 
		/// Gets an image for the current GraphPane, scaled to the specified size and resolution.
		/// 
		/// The scaled width of the bitmap in pixels
		/// The scaled height of the bitmap in pixels
		/// The resolution of the bitmap, in dots per inch
		/// 
		/// 
		/// 
		/// 
		public Bitmap GetImage( int width, int height, float dpi )
		{
			Bitmap bitmap = new Bitmap( width, height );
			bitmap.SetResolution( dpi, dpi );
			using ( Graphics bitmapGraphics = Graphics.FromImage( bitmap ) )
			{
				MakeImage( bitmapGraphics, width, height );
			}
			return bitmap;
		}
		private void MakeImage( Graphics g, int width, int height )
		{
			//g.SmoothingMode = SmoothingMode.AntiAlias;
			// This is actually a shallow clone, so we don't duplicate all the data, curveLists, etc.
			PaneBase tempPane = this.ShallowClone();
			// Clone the Chart object for GraphPanes so we don't mess up the minPix and maxPix values or
			// the rect/ChartRect calculations of the original
			//RectangleF saveRect = new RectangleF();
			//if ( this is GraphPane )
			//	saveRect = ( this as GraphPane ).Chart.Rect;
			tempPane.ReSize( g, new RectangleF( 0, 0, width, height ) );
			tempPane.Draw( g );
			//if ( this is GraphPane )
			//{
			//	GraphPane gPane = this as GraphPane;
			//	gPane.Chart.Rect = saveRect;
			//	gPane.XAxis.Scale.SetupScaleData( gPane, gPane.XAxis );
			//	foreach ( Axis axis in gPane.YAxisList )
			//		axis.Scale.SetupScaleData( gPane, axis );
			//	foreach ( Axis axis in gPane.Y2AxisList )
			//		axis.Scale.SetupScaleData( gPane, axis );
			//}
			// To restore all the various state variables, you must redraw the graph in it's
			// original form.  For this we create a 1x1 bitmap (it doesn't matter what size you use,
			// since we're only mimicing the draw.  If you use the 'bitmapGraphics' already created,
			// then you will be drawing back into the bitmap that will be returned.
			Bitmap bm = new Bitmap( 1, 1 );
			using ( Graphics bmg = Graphics.FromImage( bm ) )
			{
				this.ReSize( bmg, this.Rect );
				this.Draw( bmg );
			}
		}
		/// 
		/// Gets an enhanced metafile image for the current GraphPane, scaled to the specified size.
		/// 
		/// 
		/// By definition, a Metafile is a vector drawing, and therefore scaling should not matter.
		/// However, this method is provided because certain options in Zedgraph, such as
		///  are affected by the size of the expected image.
		/// 
		/// The "effective" scaled width of the bitmap in pixels
		/// The "effective" scaled height of the bitmap in pixels
		/// 
		/// 
		/// 
		public Metafile GetMetafile( int width, int height )
		{
			Bitmap bm = new Bitmap( 1, 1 );
			using ( Graphics g = Graphics.FromImage( bm ) )
			{
				IntPtr hdc = g.GetHdc();
				Stream stream = new MemoryStream();
				Metafile metafile = new Metafile( stream, hdc, _rect,
							MetafileFrameUnit.Pixel, EmfType.EmfPlusDual );
				g.ReleaseHdc( hdc );
				using ( Graphics metafileGraphics = Graphics.FromImage( metafile ) )
				{
					//metafileGraphics.TranslateTransform( -_rect.Left, -_rect.Top );
					metafileGraphics.PageUnit = System.Drawing.GraphicsUnit.Pixel;
					PointF P = new PointF( width, height );
					PointF[] PA = new PointF[] { P };
					metafileGraphics.TransformPoints( CoordinateSpace.Page, CoordinateSpace.Device, PA );
					//metafileGraphics.PageScale = 1f;
					// output
					MakeImage( metafileGraphics, width, height );
					//this.Draw( metafileGraphics );
					return metafile;
				}
			}
		}
		/// 
		/// Gets an enhanced metafile image for the current GraphPane.
		/// 
		/// 
		/// 
		/// 
		public Metafile GetMetafile()
		{
			Bitmap bm = new Bitmap( 1, 1 );
			using ( Graphics g = Graphics.FromImage( bm ) )
			{
				IntPtr hdc = g.GetHdc();
				Stream stream = new MemoryStream();
				Metafile metafile = new Metafile( stream, hdc, _rect,
							MetafileFrameUnit.Pixel, EmfType.EmfOnly );
				using ( Graphics metafileGraphics = Graphics.FromImage( metafile ) )
				{
					metafileGraphics.TranslateTransform( -_rect.Left, -_rect.Top );
					metafileGraphics.PageUnit = System.Drawing.GraphicsUnit.Pixel;
					PointF P = new PointF( _rect.Width, _rect.Height );
					PointF[] PA = new PointF[] { P };
					metafileGraphics.TransformPoints( CoordinateSpace.Page, CoordinateSpace.Device, PA );
					//metafileGraphics.PageScale = 1f;
					// output
					this.Draw( metafileGraphics );
					g.ReleaseHdc( hdc );
					return metafile;
				}
			}
		}
		/*
		         System.Drawing.Imaging.Metafile metafile = null; 
   
               // create a Metafile object that is compatible with the surface of this 
               // form
               using ( Graphics graphics = this.CreateGraphics() )
               { 
                  System.IntPtr hdc = graphics.GetHdc(); 
                  metafile = new Metafile(filename, hdc, new Rectangle( 0, 0, 
                     (((int) this.ClientRectangle.Width)), 
							(((int) this.ClientRectangle.Height ))), 
							MetafileFrameUnit.Point ); 
                  graphics.ReleaseHdc( hdc );
               }
               // draw to the metafile
               using ( Graphics metafileGraphics = Graphics.FromImage( metafile ) )
               {
                  metafileGraphics.PageUnit=System.Drawing.GraphicsUnit.Point;
                  PointF P=new Point(this.ClientRectangle.Width,this.ClientRectangle.Height);
                  PointF[] PA=new PointF[]{P};
                  metafileGraphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, PA); 
                  metafileGraphics.PageScale=1f;
                  metafileGraphics.SmoothingMode = SmoothingMode.AntiAlias; // smooth the 
                  // output
                  this.masterPane.Draw( metafileGraphics );
                  metafileGraphics.DrawRectangle(new System.Drawing.Pen( Color.Gray),this.ClientRectangle);
                  metafile.Dispose();
                  
               }
               return true;
            }
            else
            {
               return false;
            }
         }
         else
         {
            //no directory given
            return false;
         }
		 */
		
/*
				/// 
				/// Function to export the Diagram as WMF file
				/// see http://www.codeproject.com/showcase/pdfrasterizer.asp?print=true
				/// 
				/// 
				/// filename is the name to export to
				/// 
				public bool ExporttoWmf( string filename )
				{
					string p;
					//FileInfo TheFile = new FileInfo(filename);
					p = Path.GetDirectoryName( filename );
					if ( p != "" )
					{
						DirectoryInfo TheDir = new DirectoryInfo( p );
						if ( TheDir.Exists )
						{
							System.Drawing.Imaging.Metafile metafile = null;
							// create a Metafile object that is compatible with the surface of this 
							// form
							using ( Graphics graphics = this.CreateGraphics() )
							{
								System.IntPtr hdc = graphics.GetHdc();
								metafile = new Metafile( filename, hdc, new Rectangle( 0, 0,
									( ( (int)this.ClientRectangle.Width ) ),
									( ( (int)this.ClientRectangle.Height ) ) ),
									MetafileFrameUnit.Point );
								graphics.ReleaseHdc( hdc );
							}
							// draw to the metafile
							using ( Graphics metafileGraphics = Graphics.FromImage( metafile ) )
							{
								metafileGraphics.PageUnit = System.Drawing.GraphicsUnit.Point;
								PointF P = new Point( this.ClientRectangle.Width, this.ClientRectangle.Height );
								PointF[] PA = new PointF[] { P };
								metafileGraphics.TransformPoints( CoordinateSpace.Page, CoordinateSpace.Device, PA );
								metafileGraphics.PageScale = 1f;
								metafileGraphics.SmoothingMode = SmoothingMode.AntiAlias; // smooth the 
								// output
								this.masterPane.Draw( metafileGraphics );
								metafileGraphics.DrawRectangle( new System.Drawing.Pen( Color.Gray ), this.ClientRectangle );
								metafile.Dispose();
							}
							return true;
						}
						else
						{
							return false;
						}
					}
					else
					{
						//no directory given
						return false;
					}
				}
		*/
		internal PointF TransformCoord( double x, double y, CoordType coord )
		{
			// If the Transformation is an illegal type, just stick it in the middle
			if ( !( this is GraphPane ) && !( coord == CoordType.PaneFraction ) )
			{
				coord = CoordType.PaneFraction;
				x = 0.5;
				y = 0.5;
			}
			// Just to save some casts
			GraphPane gPane = null;
			RectangleF chartRect = new RectangleF( 0, 0, 1, 1 );
			if ( this is GraphPane )
			{
				gPane = this as GraphPane;
				chartRect = gPane.Chart._rect;
			}
			PointF ptPix = new PointF();
			if ( coord == CoordType.ChartFraction )
			{
				ptPix.X = (float)( chartRect.Left + x * chartRect.Width );
				ptPix.Y = (float)( chartRect.Top + y * chartRect.Height );
			}
			else if ( coord == CoordType.AxisXYScale )
			{
				ptPix.X = gPane.XAxis.Scale.Transform( x );
				ptPix.Y = gPane.YAxis.Scale.Transform( y );
			}
			else if ( coord == CoordType.AxisXY2Scale )
			{
				ptPix.X = gPane.XAxis.Scale.Transform( x );
				ptPix.Y = gPane.Y2Axis.Scale.Transform( y );
			}
			else if ( coord == CoordType.XScaleYChartFraction )
			{
				ptPix.X = gPane.XAxis.Scale.Transform( x );
				ptPix.Y = (float)( chartRect.Top + y * chartRect.Height );
			}
			else if ( coord == CoordType.XChartFractionYScale )
			{
				ptPix.X = (float)( chartRect.Left + x * chartRect.Width );
				ptPix.Y = gPane.YAxis.Scale.Transform( y );
			}
			else if ( coord == CoordType.XChartFractionY2Scale )
			{
				ptPix.X = (float)( chartRect.Left + x * chartRect.Width );
				ptPix.Y = gPane.Y2Axis.Scale.Transform( y );
			}
			else if ( coord == CoordType.XChartFractionYPaneFraction )
			{
				ptPix.X = (float)( chartRect.Left + x * chartRect.Width );
				ptPix.Y = (float)( this.Rect.Top + y * _rect.Height );
			}
			else if ( coord == CoordType.XPaneFractionYChartFraction )
			{
				ptPix.X = (float)( this.Rect.Left + x * _rect.Width );
				ptPix.Y = (float)( chartRect.Top + y * chartRect.Height );
			}
			else	// PaneFraction
			{
				ptPix.X = (float)( _rect.Left + x * _rect.Width );
				ptPix.Y = (float)( _rect.Top + y * _rect.Height );
			}
			return ptPix;
		}
	#endregion
	}
}