[chronojump] [autotools] Compile NPlot from sources to avoid using precompiled binaries



commit 397121ab111c5832d335b1e8b1d284e8e0179e8f
Author: Andoni Morales Alastruey <ylatuya gmail com>
Date:   Thu Aug 27 01:08:44 2009 +0200

    [autotools] Compile NPlot from sources to avoid using precompiled binaries

 Makefile.am                             |    4 +-
 configure.ac                            |    3 +
 nplot/Makefile.am                       |    3 +
 nplot/license.txt                       |   44 +
 nplot/nplot-gtk/Gtk.PlotSurface2D.cs    |  303 ++++
 nplot/nplot-gtk/Makefile.am             |   53 +
 nplot/nplot-gtk/NPlot.Gtk.dll.config    |    6 +
 nplot/nplot-gtk/sysdraw.cs              |   51 +
 nplot/nplot/AdapterUtils.cs             |  767 ++++++++
 nplot/nplot/ArrowItem.cs                |  455 +++++
 nplot/nplot/AssemblyInfo.cs             |   68 +
 nplot/nplot/AxesConstraint.cs           |  519 ++++++
 nplot/nplot/Axis.cs                     | 1654 +++++++++++++++++
 nplot/nplot/BarPlot.cs                  |  298 ++++
 nplot/nplot/BasePlot.cs                 |  138 ++
 nplot/nplot/BasePlot3D.cs               |   96 +
 nplot/nplot/BaseSequenceLinePlot.cs     |   69 +
 nplot/nplot/BaseSequencePlot.cs         |  118 ++
 nplot/nplot/Bitmap.PlotSurface2D.cs     |  541 ++++++
 nplot/nplot/BubblePlot.cs               |   72 +
 nplot/nplot/CandlePlot.cs               |  891 ++++++++++
 nplot/nplot/DateTimeAxis.cs             |  702 ++++++++
 nplot/nplot/ErrorHandler.cs             |  179 ++
 nplot/nplot/FilledRegion.cs             |  203 +++
 nplot/nplot/FontScaler.cs               |   46 +
 nplot/nplot/Grid.cs                     |  248 +++
 nplot/nplot/HistogramPlot.cs            |  521 ++++++
 nplot/nplot/HorizontalLine.cs           |  287 +++
 nplot/nplot/IDrawable.cs                |   72 +
 nplot/nplot/IDrawable3D.cs              |   27 +
 nplot/nplot/IGradient.cs                |   73 +
 nplot/nplot/IMeshPlot.cs                |   49 +
 nplot/nplot/IPlot.cs                    |  104 ++
 nplot/nplot/IPlot3D.cs                  |   33 +
 nplot/nplot/IPlotSurface2D.cs           |  255 +++
 nplot/nplot/IPlotSurface2Dnew.cs        |   19 +
 nplot/nplot/IPlotSurface3D.cs           |   17 +
 nplot/nplot/ISequencePlot.cs            |   85 +
 nplot/nplot/ISurface.cs                 |   98 +
 nplot/nplot/ITransform2D.cs             |   78 +
 nplot/nplot/ImagePlot.cs                |  369 ++++
 nplot/nplot/LabelAxis.cs                |  356 ++++
 nplot/nplot/LabelPointPlot.cs           |  324 ++++
 nplot/nplot/Legend.cs                   |  352 ++++
 nplot/nplot/LegendBase.cs               |  459 +++++
 nplot/nplot/LinePlot.cs                 |  355 ++++
 nplot/nplot/LinearAxis.cs               |  650 +++++++
 nplot/nplot/LinearGradient.cs           |  159 ++
 nplot/nplot/LogAxis.cs                  |  678 +++++++
 nplot/nplot/Makefile.am                 |   98 +
 nplot/nplot/Marker.cs                   |  487 +++++
 nplot/nplot/MarkerItem.cs               |  136 ++
 nplot/nplot/NPlotException.cs           |   90 +
 nplot/nplot/PageAlignedPhysicalAxis.cs  |  151 ++
 nplot/nplot/PhysicalAxis.cs             |  276 +++
 nplot/nplot/PiAxis.cs                   |  230 +++
 nplot/nplot/PiePlot.cs                  |  120 ++
 nplot/nplot/PlotSurface2D.cs            | 1334 ++++++++++++++
 nplot/nplot/PlotSurface2Dnew.cs         |  439 +++++
 nplot/nplot/PlotSurface3D.cs            |   64 +
 nplot/nplot/PointD.cs                   |   91 +
 nplot/nplot/PointD3D.cs                 |   45 +
 nplot/nplot/PointPlot.cs                |  182 ++
 nplot/nplot/PointPlot3D.cs              |   65 +
 nplot/nplot/RectangleBrushes.cs         | 1953 ++++++++++++++++++++
 nplot/nplot/RectangleD.cs               |  143 ++
 nplot/nplot/SequenceAdapter.cs          |  330 ++++
 nplot/nplot/SequenceAdapter3D.cs        |   28 +
 nplot/nplot/StartStep.cs                |  111 ++
 nplot/nplot/StepGradient.cs             |  149 ++
 nplot/nplot/StepPlot.cs                 |  347 ++++
 nplot/nplot/StrongName.snk              |  Bin 0 -> 596 bytes
 nplot/nplot/TextItem.cs                 |  178 ++
 nplot/nplot/TradingDateTimeAxis.cs      |  713 ++++++++
 nplot/nplot/Transform2D.cs              |  190 ++
 nplot/nplot/Utils.cs                    |  379 ++++
 nplot/nplot/VerticalLine.cs             |  293 +++
 nplot/nplot/Web.Design.PlotSurface2D.cs |  121 ++
 nplot/nplot/Web.PlotSurface2D.cs        |  669 +++++++
 nplot/nplot/Web.PlotSurface2D.cs~       |  671 +++++++
 nplot/nplot/Windows.PlotSurface.cs      |  113 ++
 nplot/nplot/Windows.PlotSurface.resx    |   42 +
 nplot/nplot/Windows.PlotSurface2D.cs    | 2942 +++++++++++++++++++++++++++++++
 nplot/nplot/Windows.PlotSurface2D.resx  |  106 ++
 nplot/nplot/Windows.PlotSurface2Dnew.cs |  155 ++
 nplot/nplot/house_rules.txt             |   94 +
 src/Makefile.am                         |   26 +-
 87 files changed, 26487 insertions(+), 25 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 7716c29..7c6d093 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,8 +6,8 @@ EXTRA_DIST = 	build/data/linux_dlls/NPlot.dll\
 ACLOCAL_AMFLAGS = -I m4
 
 if ENABLE_DEBUG
- SUBDIRS =  chronojump_server chronopic-tests  src po manual
+ SUBDIRS =  chronojump_server chronopic-tests   nplot src po manual 
 endif
 if ENABLE_RELEASE
- SUBDIRS = chronojump_server chronopic-tests  src po manual
+ SUBDIRS = chronojump_server chronopic-tests   nplot src po manual 
 endif
diff --git a/configure.ac b/configure.ac
index d5d6bac..e961d45 100644
--- a/configure.ac
+++ b/configure.ac
@@ -81,6 +81,9 @@ done
 AC_CONFIG_FILES([
 Makefile
 chronopic-tests/Makefile
+nplot/Makefile
+nplot/nplot/Makefile
+nplot/nplot-gtk/Makefile
 src/Makefile
 po/Makefile
 chronojump_server/Makefile
diff --git a/nplot/Makefile.am b/nplot/Makefile.am
new file mode 100644
index 0000000..5fd1f8f
--- /dev/null
+++ b/nplot/Makefile.am
@@ -0,0 +1,3 @@
+
+SUBDIRS =  nplot nplot-gtk
+
diff --git a/nplot/license.txt b/nplot/license.txt
new file mode 100644
index 0000000..6acea92
--- /dev/null
+++ b/nplot/license.txt
@@ -0,0 +1,44 @@
+NPlot - A charting library for .NET
+Copyright (C) 2003-2005 Matt Howlett and others.
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
diff --git a/nplot/nplot-gtk/Gtk.PlotSurface2D.cs b/nplot/nplot-gtk/Gtk.PlotSurface2D.cs
new file mode 100644
index 0000000..423469d
--- /dev/null
+++ b/nplot/nplot-gtk/Gtk.PlotSurface2D.cs
@@ -0,0 +1,303 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Data;
+
+using Gtk;
+using System.Drawing;
+
+namespace NPlot { namespace Gtk {
+
+	public class PlotSurface2D : DrawingArea, IPlotSurface2D {
+		NPlot.PlotSurface2D ps;
+		/*Axis xAxis1Cache;
+		Axis yAxis1Cache;
+		Axis xAxis2Cache;
+		Axis yAxis2Cache;*/
+
+		// The cache.
+		System.Drawing.Bitmap bitmap_cache;
+		Gdk.Rectangle current_allocation;	// The current allocation. 
+		bool allocated = false;
+
+		public PlotSurface2D (IntPtr x) : base (x)
+		{
+		Console.WriteLine (Environment.StackTrace);	
+		}
+
+		public PlotSurface2D ()
+		{
+			ps = new NPlot.PlotSurface2D ();
+			CanFocus = false;
+			SetSizeRequest (200, 200);
+		}
+
+		public void Refresh ()
+		{
+			UpdateCache ();
+			QueueDrawArea (0, 0, Allocation.Width, Allocation.Height);
+		}
+		
+		protected override bool OnExposeEvent (Gdk.EventExpose args)
+		{
+			Gdk.Rectangle area = args.Area;
+
+			Console.WriteLine (area);
+			using (Graphics g = Gdk.Graphics.FromDrawable (args.Window)){
+				Rectangle bounds = new Rectangle (area.X, area.Y, area.Width, area.Height);
+				
+				g.DrawImage (bitmap_cache, bounds, bounds, GraphicsUnit.Pixel);
+			}
+			return true;
+		}
+
+		protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+		{
+			allocated = true;
+			current_allocation = allocation;
+			UpdateCache ();
+			base.OnSizeAllocated (allocation);
+		}
+
+		void UpdateCache ()
+		{
+			if (!allocated)
+				return;
+			
+			if (bitmap_cache != null)
+				bitmap_cache.Dispose ();
+			
+			bitmap_cache = new System.Drawing.Bitmap (current_allocation.Width, current_allocation.Height);
+			using (Graphics g = Graphics.FromImage (bitmap_cache)){
+				Rectangle bounds = new Rectangle (
+					0, 0, current_allocation.Width, current_allocation.Height);
+				ps.Draw (g, bounds);
+			}
+		}
+
+#region IPlotSurface2D interface implementation
+	
+		public void Add (IDrawable p, int zOrder)
+		{
+			ps.Add (p, zOrder);
+		}
+
+		public void Add (IDrawable p)
+		{
+			ps.Add (p);
+		}
+		
+
+		public void Add (IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp, NPlot.PlotSurface2D.YAxisPosition yp)
+		{
+			ps.Add (p, xp, yp);
+		}
+
+		public void Add (NPlot.IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp, NPlot.PlotSurface2D.YAxisPosition yp, int zOrder)
+		{
+			ps.Add (p, xp, yp, zOrder);
+		}
+		
+		public void Clear()
+		{
+			ps.Clear ();
+		}
+
+		public NPlot.Legend Legend {
+			get {
+				return ps.Legend;
+			}
+			
+			set {
+				ps.Legend = value;
+			}
+		}
+
+		public int Padding {
+			get {
+				return ps.Padding;
+			}
+
+			set {
+				ps.Padding = value;
+			}
+		}
+
+		public int LegendZOrder {
+			get {
+				return ps.LegendZOrder;
+			}
+
+			set {
+				ps.LegendZOrder = value;
+			}
+		}
+
+
+		
+		public System.Drawing.Color PlotBackColor {
+			set {
+				ps.PlotBackColor = value;
+			}
+		}
+		
+		public System.Drawing.Bitmap PlotBackImage
+		{
+			set {
+				ps.PlotBackImage = value;
+			}
+		}
+
+		public IRectangleBrush PlotBackBrush
+		{
+			set {
+				ps.PlotBackBrush = value;
+			}
+		}
+
+		public Color TitleColor
+		{
+			set
+			{
+				ps.TitleColor = value;
+			}
+		}
+
+		public Brush TitleBrush
+		{
+			get
+			{
+				return ps.TitleBrush;
+			}
+			set
+			{
+				ps.TitleBrush = value;
+			}
+		}
+		
+
+		/// <summary>
+		/// Gets an array list containing all drawables currently added to the PlotSurface2D.
+		/// </summary>
+		public ArrayList Drawables
+		{
+			get
+			{
+				return ps.Drawables;
+			}
+		}
+
+		public void Remove (IDrawable p, bool updateAxes) 
+		{
+			ps.Remove(p, updateAxes);
+		}
+		
+		public string Title {
+			get {
+				return ps.Title;
+			}
+			
+			set {
+				ps.Title = value;
+			}
+		}
+		
+		public System.Drawing.Font TitleFont {
+			get {
+				return ps.TitleFont;
+			}
+
+			set {
+				ps.TitleFont = value;
+			}
+		}
+				
+		public System.Drawing.Drawing2D.SmoothingMode SmoothingMode {
+			get {
+				return ps.SmoothingMode;
+			}
+			set {
+				ps.SmoothingMode = value;
+			}
+		}
+
+		public void AddAxesConstraint (AxesConstraint c)
+		{
+				ps.AddAxesConstraint (c);
+		}
+
+		public Axis XAxis1 {
+			get {
+				return ps.XAxis1;
+			}
+			set
+				{
+					ps.XAxis1 = value;
+				}
+		}
+		
+		public Axis YAxis1 {
+			get {
+				return ps.YAxis1;
+			}
+			
+			set {
+				ps.YAxis1 = value;
+			}
+		}
+			
+		public Axis XAxis2 {
+			get {
+				return ps.XAxis2;
+			}
+
+			set {
+				ps.XAxis2 = value;
+			}
+		}
+		
+		public Axis YAxis2 {
+			get {
+				return ps.YAxis2;
+			}
+			set {
+				ps.YAxis2 = value;
+			}
+		}
+
+		public bool AutoScaleTitle {
+			get {
+				return ps.AutoScaleTitle;
+			}
+			
+			set {
+				ps.AutoScaleTitle = value;
+			}
+		}
+
+		public bool AutoScaleAutoGeneratedAxes {
+			get {
+				return ps.AutoScaleAutoGeneratedAxes;
+			}
+			
+			set {
+				ps.AutoScaleAutoGeneratedAxes = value;
+			}
+		}
+		
+		public System.Drawing.Bitmap Bitmap {
+			get { return bitmap_cache;}
+		}	  
+
+		public int Width {
+			get { return current_allocation.Width; }
+		}
+
+		public int Height {
+			get { return current_allocation.Height;}
+		}
+
+#endregion
+	
+} }
+}
diff --git a/nplot/nplot-gtk/Makefile.am b/nplot/nplot-gtk/Makefile.am
new file mode 100644
index 0000000..6def92f
--- /dev/null
+++ b/nplot/nplot-gtk/Makefile.am
@@ -0,0 +1,53 @@
+EXTRA_DIST = NPlot.Gtk.dll.config
+
+ASSEMBLY_COMPILER_COMMAND = $(GMCS)
+ASSEMBLY_COMPILER_FLAGS =  -debug+
+ASSEMBLY = bin/NPlot.Gtk.dll
+ASSEMBLY_MDB = $(ASSEMBLY).mdb
+COMPILE_TARGET = library
+BUILD_DIR = bin/
+
+RESGEN=resgen2
+
+
+all: $(ASSEMBLY) $(PROGRAMFILES)
+
+
+FILES = \
+	Gtk.PlotSurface2D.cs 		\
+	sysdraw.cs
+	
+
+
+REFERENCES =  \
+		System.Web \
+		System.Design \
+		System.Drawing \
+		System.Data \
+		$(GTK_SHARP_20_LIBS)
+	
+	
+DLL_REFERENCES = ../nplot/bin/NPlot.dll
+
+PROGRAMFILES = \
+	$(NPLOT_GTK_DLL_CONFIG)
+
+CLEANFILES = $(PROGRAMFILES) 
+
+include $(top_srcdir)/Makefile.include
+
+NPLOT_GTK_DLL_CONFIG_SOURCE=./NPlot.Gtk.dll.config
+NPLOT_GTK_DLL_CONFIG=$(BUILD_DIR)/NPlot.Gtk.dll.config
+$(eval $(call emit-deploy-target,NPLOT_GTK_DLL_CONFIG))
+
+$(eval $(call emit_resgen_targets))
+$(build_xamlg_list): %.xaml.g.cs: %.xaml
+	xamlg '$<'
+
+$(ASSEMBLY) $(ASSEMBLY_MDB): $(build_sources) $(build_resources) $(build_datafiles) $(DLL_REFERENCES) $(PROJECT_REFERENCES) $(build_xamlg_list) $(build_satellite_assembly_list)
+	mkdir -p $(shell dirname $(ASSEMBLY))
+	$(ASSEMBLY_COMPILER_COMMAND) $(ASSEMBLY_COMPILER_FLAGS) -out:$(ASSEMBLY) -target:$(COMPILE_TARGET) $(build_sources_embed) $(build_resources_embed) $(build_references_ref)
+
+
+
+
diff --git a/nplot/nplot-gtk/NPlot.Gtk.dll.config b/nplot/nplot-gtk/NPlot.Gtk.dll.config
new file mode 100644
index 0000000..3b2b4ff
--- /dev/null
+++ b/nplot/nplot-gtk/NPlot.Gtk.dll.config
@@ -0,0 +1,6 @@
+<configuration>
+  <dllmap dll="libglib-2.0-0.dll" target="libglib-2.0.so.0"/>
+  <dllmap dll="libgobject-2.0-0.dll" target="libgobject-2.0.so.0"/>
+  <dllmap dll="libgdk-win32-2.0-0.dll" target="libgdk-x11-2.0.so.0"/>
+  <dllmap dll="libgdk_pixbuf-2.0-0.dll" target="libgdk_pixbuf-2.0.so.0"/>
+</configuration>
diff --git a/nplot/nplot-gtk/sysdraw.cs b/nplot/nplot-gtk/sysdraw.cs
new file mode 100644
index 0000000..cc04674
--- /dev/null
+++ b/nplot/nplot-gtk/sysdraw.cs
@@ -0,0 +1,51 @@
+//
+// System.Drawing integration with Gtk#
+//
+// Miguel de Icaza
+//
+// API issues:
+//    Maybe make the translation `out' parameters so they are explicit and the user knows about it?
+//    Add a way to copy a Graphics into a drawable?
+//
+
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace Gdk {
+	public class Graphics {
+		
+		[DllImport("libgdk-win32-2.0-0.dll")]
+		internal static extern IntPtr gdk_x11_drawable_get_xdisplay (IntPtr raw);
+		
+		[DllImport("libgdk-win32-2.0-0.dll")]
+		internal static extern IntPtr gdk_x11_drawable_get_xid (IntPtr raw);
+		
+		public static System.Drawing.Graphics FromDrawable (Gdk.Drawable drawable)
+		{
+			IntPtr x_drawable;
+			int x_off = 0, y_off = 0;
+				
+			
+			if (drawable is Gdk.Window){
+				((Gdk.Window) drawable).GetInternalPaintInfo(out drawable, out x_off, out y_off);
+			} 
+			x_drawable = drawable.Handle;
+			
+			IntPtr display = gdk_x11_drawable_get_xdisplay (x_drawable);
+			
+			Type graphics = typeof (System.Drawing.Graphics);
+			MethodInfo mi = graphics.GetMethod ("FromXDrawable", BindingFlags.Static | BindingFlags.NonPublic);
+			if (mi == null)
+				throw new NotImplementedException ("In this implementation I can not get a graphics from a drawable");
+			object [] args = new object [2] { (IntPtr) gdk_x11_drawable_get_xid (drawable.Handle), (IntPtr) display };
+			object r = mi.Invoke (null, args);
+			System.Drawing.Graphics g = (System.Drawing.Graphics) r;
+
+			g.TranslateTransform (-x_off, -y_off);
+
+			return g;
+		}
+	}
+
+}
diff --git a/nplot/nplot/AdapterUtils.cs b/nplot/nplot/AdapterUtils.cs
new file mode 100644
index 0000000..9a147d7
--- /dev/null
+++ b/nplot/nplot/AdapterUtils.cs
@@ -0,0 +1,767 @@
+/*
+NPlot - A charting library for .NET
+
+AdapterUtils.cs
+Copyright (C) 2003-2005
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
+using System.Data;
+
+namespace NPlot
+{
+
+    /// <summary>
+    /// Encapsulates functionality relating to exposing data in various
+    /// different data structures in a consistent way.
+    /// </summary>
+    /// <remarks>It would be more efficient to have iterator style access
+    /// to the data, rather than index based, and Count.</remarks>
+	public class AdapterUtils
+	{
+
+		#region AxisSuggesters
+
+		/// <summary>
+		/// Interface for classes that can suggest an axis for data they contain.
+		/// </summary>
+		public interface IAxisSuggester
+		{
+			/// <summary>
+			/// Calculates a suggested axis for the data contained by the implementing class.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+			Axis Get();
+		}
+
+
+		/// <summary>
+		/// Implements functionality for suggesting an axis suitable for charting 
+		/// data in multiple columns of a DataRowCollection.
+		/// </summary>
+		/// <remarks>This is currently not used.</remarks>
+		public class AxisSuggester_MultiColumns : IAxisSuggester
+		{			
+
+			DataRowCollection rows_;
+			string abscissaName_;
+
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="rows">The DataRowCollection containing the data.</param>
+			/// <param name="abscissaName">the column with this name is not considered</param>
+			public AxisSuggester_MultiColumns(DataRowCollection rows, string abscissaName)
+			{
+				rows_ = rows;
+				abscissaName_ = abscissaName;
+			}
+
+
+			/// <summary>
+			/// Calculates a suggested axis for the DataRowCollection data.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+			public Axis Get()
+			{
+				double t_min = double.MaxValue;
+				double t_max = double.MinValue;
+
+				System.Collections.IEnumerator en = rows_[0].Table.Columns.GetEnumerator();
+				
+				while (en.MoveNext())
+				{
+					string colName = ((DataColumn)en.Current).Caption;
+					
+					if (colName == abscissaName_)
+					{
+						continue;
+					}
+
+					double min;
+					double max;
+					if (Utils.RowArrayMinMax(rows_, out min, out max, colName))
+					{
+						if (min < t_min)
+						{
+							t_min = min;
+						}
+						if (max > t_max)
+						{
+							t_max = max;
+						}
+					}
+				}
+
+				return new LinearAxis(t_min, t_max);
+			}
+		
+		}
+
+	
+        /// <summary>
+        /// This class gets an axis suitable for plotting the data contained in an IList.
+        /// </summary>
+        public class AxisSuggester_IList : IAxisSuggester
+        {
+            private IList data_;
+
+            /// <summary>
+            /// Constructor. 
+            /// </summary>
+            /// <param name="data">the data we want to find a suitable axis for.</param>
+            public AxisSuggester_IList(IList data)
+            {
+                data_ = data;
+            }
+
+			/// <summary>
+			/// Calculates a suggested axis for the IList data.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+            public Axis Get()
+            {
+                double min;
+                double max;
+
+                if (Utils.ArrayMinMax(data_, out min, out max))
+                {
+                    if (data_[0] is DateTime)
+                    {
+                        return new DateTimeAxis(min, max);
+                    }
+
+                    else
+                    {
+                        return new LinearAxis(min, max);
+                    }
+
+                    // perhaps return LogAxis here if range large enough 
+                    // + other constraints?
+                }
+
+                return new LinearAxis(0.0, 1.0);
+            }
+        }
+
+
+        /// <summary>
+        /// This class is responsible for supplying a default axis via the IAxisSuggester interface.
+        /// </summary>
+        public class AxisSuggester_Null : IAxisSuggester
+        {
+			/// <summary>
+			/// Returns a default axis.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+            public Axis Get()
+            {
+                return new LinearAxis(0.0, 1.0);
+            }
+        }
+
+
+		/// <summary>
+		/// This class gets an axis corresponding to a StartStep object. The data on
+		/// the orthogonal axis is of course also needed to calculate this.
+		/// </summary>
+        public class AxisSuggester_StartStep : IAxisSuggester
+        {
+            StartStep abscissaData_;
+            IList ordinateData_;
+
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="axisOfInterest">StartStep object corresponding to axis of interest</param>
+            /// <param name="otherAxisData">data of other axis (needed to get count value)</param>
+            public AxisSuggester_StartStep(StartStep axisOfInterest, IList otherAxisData)
+            {
+                ordinateData_ = otherAxisData;
+                abscissaData_ = axisOfInterest;
+            }
+
+			/// <summary>
+			/// Calculates a suggested axis given the data specified in the constructor.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+            public Axis Get()
+            {
+                return new LinearAxis(
+                    abscissaData_.Start,
+                    abscissaData_.Start + (double)(ordinateData_.Count - 1) * abscissaData_.Step);
+            }
+        }
+
+
+        /// <summary>
+        /// Provides default axis if only data corresponding to orthogonal axis is provided.
+        /// </summary>
+        public class AxisSuggester_Auto : IAxisSuggester
+        {
+            
+			IList ordinateData_;
+
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="ordinateData">Data corresponding to orthogonal axis.</param>
+            public AxisSuggester_Auto(IList ordinateData)
+            {
+                ordinateData_ = ordinateData;
+            }
+
+			/// <summary>
+			/// Calculates a suggested axis given the data specified in the constructor.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+            public Axis Get()
+            {
+                return new LinearAxis(0, ordinateData_.Count - 1);
+            }
+        }
+
+
+        /// <summary>
+        /// Provides default axis if only data corresponding to orthogonal axis is provided.
+        /// </summary>
+        public class AxisSuggester_RowAuto : IAxisSuggester
+        {
+            DataRowCollection ordinateData_;
+
+            /// <summary>
+            /// Construbtor
+            /// </summary>
+            /// <param name="ordinateData">Data corresponding to orthogonal axis.</param>
+            public AxisSuggester_RowAuto(DataRowCollection ordinateData)
+            {
+                ordinateData_ = ordinateData;
+            }
+
+			/// <summary>
+			/// Calculates a suggested axis given the data specified in the constructor.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+            public Axis Get()
+            {
+                return new LinearAxis(0, ordinateData_.Count - 1);
+            }
+        }
+
+        /// <summary>
+        /// Provides axis for data in a given column of a DataRowCollection.
+        /// </summary>
+        public class AxisSuggester_Rows : IAxisSuggester
+        {
+            DataRowCollection rows_;
+            string columnName_;
+
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="rows">DataRowCollection containing the data to suggest axis for.</param>
+            /// <param name="columnName">the column to get data.</param>
+            public AxisSuggester_Rows(DataRowCollection rows, string columnName)
+            {
+                rows_ = rows;
+                columnName_ = columnName;
+            }
+
+			/// <summary>
+			/// Calculates a suggested axis given the data specified in the constructor.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+            public Axis Get()
+            {
+                double min;
+                double max;
+
+                if (Utils.RowArrayMinMax(rows_, out min, out max, columnName_))
+                {
+                    if ((rows_[0])[columnName_] is DateTime)
+                    {
+                        return new DateTimeAxis(min, max);
+                    }
+
+                    else
+                    {
+                        return new LinearAxis(min, max);
+                    }
+                }
+
+                return new LinearAxis(0.0, 1.0);
+            }
+        }
+
+
+        /// <summary>
+        /// Provides axis suggestion for data in a particular column of a DataView.
+        /// </summary>
+        public class AxisSuggester_DataView : IAxisSuggester
+        {
+            DataView data_;
+            string columnName_;
+
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="data">DataView that contains data to suggest axis for</param>
+            /// <param name="columnName">the column of interest in the DataView</param>
+            public AxisSuggester_DataView(DataView data, string columnName)
+            {
+                data_ = data;
+                columnName_ = columnName;
+            }
+
+			/// <summary>
+			/// Calculates a suggested axis given the data specified in the constructor.
+			/// </summary>
+			/// <returns>the suggested axis</returns>
+            public Axis Get()
+            {
+                double min;
+                double max;
+
+                if (Utils.DataViewArrayMinMax(data_, out min, out max, columnName_))
+                {
+                    if ((data_[0])[columnName_] is DateTime)
+                    {
+                        return new DateTimeAxis(min, max);
+                    }
+
+                    else
+                    {
+                        return new LinearAxis(min, max);
+                    }
+                }
+
+                return new LinearAxis(0.0, 1.0);
+            }
+        }
+
+        #endregion
+        #region Counters
+
+        /// <summary>
+        /// Interface that enables a dataholding class to report how many data items it holds.
+        /// </summary>
+        public interface ICounter
+        {
+            /// <summary>
+            /// Number of data items in container.
+            /// </summary>
+            /// <value>Number of data items in container.</value>
+            int Count { get; }
+        }
+
+        /// <summary>
+        /// Class that provides the number of items in an IList via the ICounter interface.
+        /// </summary>
+        public class Counter_IList : ICounter
+        {
+            private IList data_;
+
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="data">the IList data to provide count of</param>
+            public Counter_IList(IList data)
+            {
+                data_ = data;
+            }
+
+			/// <summary>
+			/// Number of data items in container.
+			/// </summary>
+			/// <value>Number of data items in container.</value>
+            public int Count
+            {
+                get
+                {
+                    return data_.Count;
+                }
+            }
+        }
+
+		/// <summary>
+		/// Class that returns 0 via the ICounter interface.
+		/// </summary>
+        public class Counter_Null : ICounter
+        {
+			/// <summary>
+			/// Number of data items in container.
+			/// </summary>
+			/// <value>Number of data items in container.</value>
+            public int Count
+            {
+                get
+                {
+                    return 0;
+                }
+            }
+        }
+
+		/// <summary>
+		/// Class that provides the number of items in a DataRowCollection via the ICounter interface.
+		/// </summary>
+        public class Counter_Rows : ICounter
+        {
+            DataRowCollection rows_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="rows">the DataRowCollection data to provide count of number of rows of.</param>
+            public Counter_Rows(DataRowCollection rows)
+            {
+                rows_ = rows;
+            }
+
+			/// <summary>
+			/// Number of data items in container.
+			/// </summary>
+			/// <value>Number of data items in container.</value>
+            public int Count
+            {
+                get
+                {
+                    return rows_.Count;
+                }
+            }
+        }
+
+		/// <summary>
+		/// Class that provides the number of items in a DataView via the ICounter interface.
+		/// </summary>
+        public class Counter_DataView : ICounter
+        {
+            DataView dataView_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="dataView">the DataBiew data to provide count of number of rows of.</param>
+            public Counter_DataView(DataView dataView)
+            {
+                dataView_ = dataView;
+            }
+
+			/// <summary>
+			/// Number of data items in container.
+			/// </summary>
+			/// <value>Number of data items in container.</value>
+            public int Count
+            {
+                get
+                {
+                    return dataView_.Count;
+                }
+            }
+
+        }
+
+        #endregion
+        #region DataGetters
+
+        /// <summary>
+        /// Interface for data holding classes that allows users to get the ith value.
+        /// </summary>
+		/// <remarks>
+		/// TODO: should change this to GetNext() and Reset() for more generality.
+		/// </remarks>
+		public interface IDataGetter
+        {
+            /// <summary>
+            /// Gets the ith data value.
+            /// </summary>
+            /// <param name="i">sequence number of data to get.</param>
+            /// <returns>ith data value.</returns>
+            double Get(int i);
+        }
+
+        /// <summary>
+        /// Provides data in an IList via the IDataGetter interface.
+        /// </summary>
+        public class DataGetter_IList : IDataGetter
+        {
+            private IList data_;
+            
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="data">IList that contains the data</param>
+            public DataGetter_IList(IList data)
+            {
+                data_ = data;
+            }
+
+			/// <summary>
+			/// Gets the ith data value.
+			/// </summary>
+			/// <param name="i">sequence number of data to get.</param>
+			/// <returns>ith data value.</returns>
+            public double Get(int i)
+            {
+                return Utils.ToDouble(data_[i]);
+            }
+        }
+
+
+        /// <summary>
+        /// Provides data in an array of doubles via the IDataGetter interface.
+        /// </summary>
+        /// <remarks>
+        /// A speed-up version of DataDetter_IList; no boxing/unboxing overhead.
+        /// </remarks>
+		public class DataGetter_DoublesArray : IDataGetter
+		{
+			private double[] data_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="data">array of doubles that contains the data</param>
+			public DataGetter_DoublesArray(double[] data)          
+			{
+				data_ = data;
+			}
+		
+			/// <summary>
+			/// Gets the ith data value.
+			/// </summary>
+			/// <param name="i">sequence number of data to get.</param>
+			/// <returns>ith data value.</returns>
+			public double Get(int i)          
+			{
+				return data_[i];
+			}
+		}
+
+
+		/// <summary>
+		/// Provides no data.
+		/// </summary>
+        public class DataGetter_Null : IDataGetter
+        {
+			/// <summary>
+			/// Gets the ith data value.
+			/// </summary>
+			/// <param name="i">sequence number of data to get.</param>
+			/// <returns>ith data value.</returns>
+            public double Get(int i)
+            {
+                throw new NPlotException( "No Data!" );
+            }
+        }
+
+		/// <summary>
+		/// Provides data points from a StartStep object via the IDataGetter interface.
+		/// </summary>
+        public class DataGetter_StartStep : IDataGetter
+        {
+            StartStep data_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="data">StartStep to derive data from.</param>
+            public DataGetter_StartStep(StartStep data)
+            {
+                data_ = data;
+            }
+
+			/// <summary>
+			/// Gets the ith data value.
+			/// </summary>
+			/// <param name="i">sequence number of data to get.</param>
+			/// <returns>ith data value.</returns>
+            public double Get(int i)
+            {
+                return data_.Start + (double)(i) * data_.Step;
+            }
+        }
+
+		/// <summary>
+		/// Provides the natural numbers (and 0) via the IDataGetter interface.
+		/// </summary>
+        public class DataGetter_Count : IDataGetter
+        {
+			/// <summary>
+			/// Gets the ith data value.
+			/// </summary>
+			/// <param name="i">sequence number of data to get.</param>
+			/// <returns>ith data value.</returns>
+            public double Get(int i)
+            {
+                return (double)i;
+            }
+        }
+
+
+		/// <summary>
+		/// Provides data in a DataRowCollection via the IDataGetter interface.
+		/// </summary>
+        public class DataGetter_Rows : IDataGetter
+        {
+            private DataRowCollection rows_;
+            private string columnName_;
+
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="rows">DataRowCollection to get data from</param>
+            /// <param name="columnName">Get data in this column</param>
+            public DataGetter_Rows(DataRowCollection rows, string columnName)
+            {
+                rows_ = rows;
+                columnName_ = columnName;
+            }
+
+			/// <summary>
+			/// Gets the ith data value.
+			/// </summary>
+			/// <param name="i">sequence number of data to get.</param>
+			/// <returns>ith data value.</returns>
+            public double Get(int i)
+            {
+                return Utils.ToDouble((rows_[i])[columnName_]);
+            }
+        }
+
+		/// <summary>
+		/// Provides data in a DataView via the IDataGetter interface.
+		/// </summary>
+        public class DataGetter_DataView : IDataGetter
+        {
+            private DataView data_;
+            private string columnName_;
+
+            /// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="data">DataView to get data from.</param>
+            /// <param name="columnName">Get data in this column</param>
+            public DataGetter_DataView(DataView data, string columnName)
+            {
+                data_ = data;
+                columnName_ = columnName;
+            }
+
+			/// <summary>
+			/// Gets the ith data value.
+			/// </summary>
+			/// <param name="i">sequence number of data to get.</param>
+			/// <returns>ith data value.</returns>
+            public double Get(int i)
+            {
+                return Utils.ToDouble((data_[i])[columnName_]);
+            }
+
+        }
+
+		
+		/// <summary>
+		/// Gets data 
+		/// </summary>
+		/// <remarks>Note: Does not implement IDataGetter... Currently this class is not used.</remarks>
+		public class DataGetter_MultiRows 
+		{
+			
+			DataRowCollection rows_;
+			string abscissaName_;
+			int abscissaColumnNumber_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="rows">DataRowCollection to get data from.</param>
+			/// <param name="omitThisColumn">don't get data from this column</param>
+			public DataGetter_MultiRows(DataRowCollection rows, string omitThisColumn )
+			{
+				rows_ = rows;
+				abscissaName_ = omitThisColumn;
+
+				abscissaColumnNumber_ = rows_[0].Table.Columns.IndexOf( omitThisColumn );
+				if (abscissaColumnNumber_ < 0)
+					throw new NPlotException( "invalid column name" );
+			}
+
+			/// <summary>
+			/// Number of data points
+			/// </summary>
+			public int Count
+			{
+				get
+				{
+					return rows_[0].Table.Columns.Count-1;
+				}
+			}
+
+			/// <summary>
+			/// Gets data at a given index, in the given series (column number).
+			/// </summary>
+			/// <param name="index">index in the series to get data for</param>
+			/// <param name="seriesIndex">series number (column number) to get data for.</param>
+			/// <returns>the required data point.</returns>
+			public double PointAt( int index, int seriesIndex )
+			{
+				if (seriesIndex < abscissaColumnNumber_)
+					return Utils.ToDouble( rows_[index][seriesIndex] );
+				else
+					return Utils.ToDouble( rows_[index][seriesIndex+1] );
+			}
+
+		}
+
+
+        #endregion
+
+    }
+}
diff --git a/nplot/nplot/ArrowItem.cs b/nplot/nplot/ArrowItem.cs
new file mode 100644
index 0000000..b940bee
--- /dev/null
+++ b/nplot/nplot/ArrowItem.cs
@@ -0,0 +1,455 @@
+/*
+NPlot - A charting library for .NET
+
+ArrowItem.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// An Arrow IDrawable, with a text label that is automatically
+	/// nicely positioned at the non-pointy end of the arrow. Future
+	/// feature idea: have constructor that takes a dataset, and have
+	/// the arrow know how to automatically set it's angle to avoid 
+	/// the data.
+	/// </summary>
+	public class ArrowItem : IDrawable
+	{
+
+		private void Init()
+		{
+			FontFamily fontFamily = new FontFamily("Arial");
+			font_ = new Font(fontFamily, 10, FontStyle.Regular, GraphicsUnit.Pixel);
+		}
+
+
+		/// <summary>
+		/// Default constructor : 
+		/// text = ""
+		/// angle = 45 degrees anticlockwise from horizontal.
+		/// </summary>
+		/// <param name="position">The position the arrow points to.</param>
+		public ArrowItem( PointD position )
+		{
+			to_ = position;
+			Init();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="position">The position the arrow points to.</param>
+		/// <param name="angle">angle of arrow with respect to x axis.</param>
+		public ArrowItem( PointD position, double angle )
+		{
+			to_ = position;
+			angle_ = -angle;
+			Init();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="position">The position the arrow points to.</param>
+		/// <param name="angle">angle of arrow with respect to x axis.</param>
+		/// <param name="text">The text associated with the arrow.</param>
+		public ArrowItem( PointD position, double angle, string text )
+		{
+			to_ = position;
+			angle_ = -angle;
+			text_ = text;
+			Init();
+		}
+
+
+		/// <summary>
+		/// Text associated with the arrow.
+		/// </summary>
+		public string Text 
+		{
+			get
+			{
+				return text_;
+			}
+			set
+			{
+				text_ = value;
+			}
+		}
+		private string text_ = "";
+
+
+		/// <summary>
+		/// Angle of arrow anti-clockwise to right horizontal in degrees.
+		/// </summary>
+		/// <remarks>The code relating to this property in the Draw method is
+		/// a bit weird. Internally, all rotations are clockwise [this is by 
+		/// accient, I wasn't concentrating when I was doing it and was half
+		/// done before I realised]. The simplest way to make angle represent
+		/// anti-clockwise rotation (as it is normal to do) is to make the 
+		/// get and set methods negate the provided value.</remarks>
+		public double Angle
+		{
+			get
+			{
+				return -angle_;
+			}
+			set
+			{
+				angle_ = -value;
+			}
+		}
+		private double angle_ = -45.0;
+
+
+		/// <summary>
+		/// Physical length of the arrow. 
+		/// </summary>
+		public float PhysicalLength
+		{
+			get
+			{
+				return physicalLength_;
+			}
+			set
+			{
+				physicalLength_ = value;
+			}
+		}
+		private float physicalLength_ = 40.0f;
+
+
+		/// <summary>
+		/// The point the arrow points to.
+		/// </summary>
+		public PointD To
+		{
+			get
+			{
+				return to_;
+			}
+			set
+			{
+				to_ = value;
+			}
+		}
+		private PointD to_;
+
+
+		/// <summary>
+		/// Size of the arrow head sides in pixels.
+		/// </summary>
+		public float HeadSize
+		{
+			get
+			{
+				return headSize_;
+			}
+			set
+			{
+				headSize_ = value;
+			}
+		}
+		private float headSize_ = 10.0f;
+
+
+		/// <summary>
+		/// angle between sides of arrow head in degrees
+		/// </summary>
+		public float HeadAngle
+		{
+			get
+			{
+				return headAngle_;
+			}
+			set
+			{
+				headAngle_ = value;
+			}
+		}
+		private float headAngle_ = 40.0f;
+
+
+		/// <summary>
+		/// Draws the arrow on a plot surface.
+		/// </summary>
+		/// <param name="g">graphics surface on which to draw</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+            if (this.To.X > xAxis.Axis.WorldMax || this.To.X < xAxis.Axis.WorldMin)
+                return;
+
+            if (this.To.Y > yAxis.Axis.WorldMax || this.To.Y < yAxis.Axis.WorldMin)
+                return;
+
+            double angle = this.angle_;
+
+			if (this.angle_ < 0.0)
+			{
+				int mul = -(int)(this.angle_ / 360.0) + 2;
+				angle = angle_ + 360.0 * (double)mul;
+			}
+
+			double normAngle = (double)angle % 360.0;   // angle in range 0 -> 360.
+
+			Point toPoint = new Point( 
+				(int)xAxis.WorldToPhysical( to_.X, true ).X,
+				(int)yAxis.WorldToPhysical( to_.Y, true ).Y );
+
+
+			float xDir = (float)Math.Cos( normAngle * 2.0 * Math.PI / 360.0 );
+			float yDir = (float)Math.Sin( normAngle * 2.0 * Math.PI / 360.0 );
+
+			toPoint.X += (int)(xDir*headOffset_);
+			toPoint.Y += (int)(yDir*headOffset_);
+
+			float xOff = physicalLength_ * xDir;
+			float yOff = physicalLength_ * yDir;
+
+			Point fromPoint = new Point(
+				(int)(toPoint.X + xOff),
+				(int)(toPoint.Y + yOff) );
+
+			g.DrawLine( pen_, toPoint, fromPoint );
+
+			Point[] head = new Point[3];
+
+			head[0] = toPoint;
+
+			xOff = headSize_ * (float)Math.Cos( (normAngle-headAngle_/2.0f) * 2.0 * Math.PI / 360.0 );
+			yOff = headSize_ * (float)Math.Sin( (normAngle-headAngle_/2.0f) * 2.0 * Math.PI / 360.0 );
+
+			head[1] = new Point(
+				(int)(toPoint.X + xOff),
+				(int)(toPoint.Y + yOff) );
+
+			float xOff2 = headSize_ * (float)Math.Cos( (normAngle+headAngle_/2.0f) * 2.0 * Math.PI / 360.0 );
+			float yOff2 = headSize_ * (float)Math.Sin( (normAngle+headAngle_/2.0f) * 2.0 * Math.PI / 360.0 );
+
+			head[2] = new Point(
+				(int)(toPoint.X + xOff2),
+				(int)(toPoint.Y + yOff2) );
+
+			g.FillPolygon( arrowBrush_, head );
+
+			SizeF textSize = g.MeasureString( text_, font_ );
+			SizeF halfSize = new SizeF( textSize.Width / 2.0f, textSize.Height / 2.0f );
+
+			float quadrantSlideLength = halfSize.Width + halfSize.Height;
+
+			float quadrantF = (float)normAngle / 90.0f;       // integer part gives quadrant.
+			int quadrant = (int)quadrantF;	          // quadrant in. 
+			float prop = quadrantF - (float)quadrant; // proportion of way through this qadrant. 
+			float dist = prop * quadrantSlideLength;	  // distance along quarter of bounds rectangle.
+			
+			// now find the offset from the middle of the text box that the
+			// rear end of the arrow should end at (reverse this to get position
+			// of text box with respect to rear end of arrow).
+			//
+			// There is almost certainly an elgant way of doing this involving
+			// trig functions to get all the signs right, but I'm about ready to 
+			// drop off to sleep at the moment, so this blatent method will have 
+			// to do.
+			PointF offsetFromMiddle = new PointF( 0.0f, 0.0f );
+			switch (quadrant)
+			{
+				case 0:
+					if (dist > halfSize.Height)
+					{
+						dist -= halfSize.Height;
+						offsetFromMiddle = new PointF( -halfSize.Width + dist, halfSize.Height );
+					}
+					else
+					{
+						offsetFromMiddle = new PointF( -halfSize.Width, - dist );
+					}
+					break;
+
+				case 1:
+					if (dist > halfSize.Width)
+					{
+						dist -= halfSize.Width;
+						offsetFromMiddle = new PointF( halfSize.Width, halfSize.Height - dist );
+					}
+					else
+					{
+						offsetFromMiddle = new PointF( dist, halfSize.Height );
+					}
+					break;
+
+				case 2:
+					if (dist > halfSize.Height)
+					{
+						dist -= halfSize.Height;
+						offsetFromMiddle = new PointF( halfSize.Width - dist, -halfSize.Height );
+					}
+					else
+					{
+						offsetFromMiddle = new PointF( halfSize.Width, -dist );
+					}
+
+					break;
+
+				case 3:
+					if (dist > halfSize.Width)
+					{
+						dist -= halfSize.Width;
+						offsetFromMiddle = new PointF( -halfSize.Width, -halfSize.Height + dist );
+					}
+					else
+					{
+						offsetFromMiddle = new PointF( -dist, -halfSize.Height );
+					}
+
+					break;
+
+				default:
+					throw new NPlotException( "Programmer error." );
+
+			}
+
+			g.DrawString( 
+				text_, font_, textBrush_,
+				(int)(fromPoint.X - halfSize.Width - offsetFromMiddle.X),
+				(int)(fromPoint.Y - halfSize.Height + offsetFromMiddle.Y) );
+
+		}
+
+
+		/// <summary>
+		/// The brush used to draw the text associated with the arrow.
+		/// </summary>
+		public Brush TextBrush
+		{
+			get
+			{
+				return textBrush_;
+			}
+			set
+			{
+				textBrush_ = value;
+			}
+		}
+
+	
+		/// <summary>
+		/// Set the text to be drawn with a solid brush of this color.
+		/// </summary>
+		public Color TextColor
+		{
+			set
+			{
+				textBrush_ = new SolidBrush( value );
+			}
+		}
+
+
+		/// <summary>
+		/// The color of the pen used to draw the arrow.
+		/// </summary>
+		public Color ArrowColor
+		{
+			get
+			{
+				return pen_.Color;
+			}
+			set
+			{
+				pen_.Color = value;
+				arrowBrush_ = new SolidBrush( value );
+			}
+		}
+			
+		
+		/// <summary>
+		/// The font used to draw the text associated with the arrow.
+		/// </summary>
+		public Font TextFont
+		{
+			get
+			{
+				return this.font_;
+			}
+			set
+			{
+				this.font_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Offset the whole arrow back in the arrow direction this many pixels from the point it's pointing to.
+		/// </summary>
+		public int HeadOffset
+		{
+			get
+			{
+				return headOffset_;
+			}
+			set
+			{
+				headOffset_ = value;
+			}
+		}
+		private int headOffset_ = 2;
+
+
+		private Brush arrowBrush_ = new SolidBrush( Color.Black );
+		private Brush textBrush_ = new SolidBrush( Color.Black );
+		private Pen pen_ = new Pen( Color.Black );
+		private Font font_;
+	}
+}
diff --git a/nplot/nplot/AssemblyInfo.cs b/nplot/nplot/AssemblyInfo.cs
new file mode 100644
index 0000000..a960954
--- /dev/null
+++ b/nplot/nplot/AssemblyInfo.cs
@@ -0,0 +1,68 @@
+/*
+NPlot - A charting library for .NET
+
+AssemblyInfo.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle("NPlot")]
+[assembly: AssemblyDescription("NPlot Charting Library")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("netcontrols")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: AssemblyVersion("0.9.9.2")]
+[assembly: CLSCompliant(true)]
+[assembly: AssemblyKeyFile("StrongName.snk")]
+
diff --git a/nplot/nplot/AxesConstraint.cs b/nplot/nplot/AxesConstraint.cs
new file mode 100644
index 0000000..8518552
--- /dev/null
+++ b/nplot/nplot/AxesConstraint.cs
@@ -0,0 +1,519 @@
+/*
+NPlot - A charting library for .NET
+
+AxesConstraint.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Classes derived from this abstract base class define and can apply 
+	/// some form of constraint to the positioning and length of one or more
+	/// of the four axes of a PlotSurface2D.
+	/// </summary>
+	public abstract class AxesConstraint
+	{
+
+		/// <summary>
+		/// Defines an AxisConstraint that forces the world length corresponding
+		/// to one pixel on the bottom x-axis to be a certain value. 
+		/// 
+		/// TODO: Allow the pixel world length to be set for the top axis.
+		/// </summary>
+		public class XPixelWorldLength : AxesConstraint
+		{
+			private double pWorldLength_ = 0.0f;
+			private object holdFixedY_ = null;
+
+			/// <summary>
+			/// Constructor, which defines the world pixel length only. Both 
+			/// y-axes will be moved by equal amounts in order to force this
+			/// constraint.
+			/// </summary>
+			/// <param name="p">The world pixel length</param>
+			public XPixelWorldLength( double p )
+			{
+				this.pWorldLength_ = p;
+			}
+		
+			/// <summary>
+			/// Constructor, which defines the world pixel length together with
+			/// the y-axis that should be held constant when forcing this 
+			/// constraint [the other y-axis only will be moved].
+			/// </summary>
+			/// <param name="p">The world pixel length</param>
+			/// <param name="holdFixedY">The position of this y-axis will be 
+			/// held constant. The other y-axis will be moved in order to 
+			/// force the constraint.</param>
+			public XPixelWorldLength( double p, PlotSurface2D.YAxisPosition holdFixedY )
+			{
+				this.pWorldLength_ = p;
+				this.holdFixedY_ = holdFixedY;
+			}
+
+			/// <summary>
+			/// Applies the constraint to the axes.
+			/// </summary>
+			/// <param name="pXAxis1">The bottom x-axis.</param>
+			/// <param name="pYAxis1">The left y-axis.</param>
+			/// <param name="pXAxis2">The top x-axis.</param>
+			/// <param name="pYAxis2">The right y-axis.</param>
+			public override void ApplyConstraint( 
+				PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, 
+				PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 )
+			{
+				int desiredLength = (int)(pXAxis1.Axis.WorldLength / (double)this.pWorldLength_);
+				int currentLength = pXAxis1.PhysicalLength;
+				int delta = currentLength - desiredLength;
+
+				int changeLeft = delta / 2;
+				int changeRight = delta / 2;
+				if (this.holdFixedY_ != null)
+				{
+					if ( (PlotSurface2D.YAxisPosition)this.holdFixedY_ == PlotSurface2D.YAxisPosition.Left )
+					{
+						changeLeft = 0;
+						changeRight = delta;
+					}
+					else
+					{
+						changeLeft = delta;
+						changeRight = 0;
+					}
+				}
+
+				pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X+changeLeft, pXAxis1.PhysicalMin.Y );
+				pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X-changeRight, pXAxis1.PhysicalMax.Y );
+				pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X+changeLeft, pXAxis2.PhysicalMin.Y );
+				pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X-changeRight, pXAxis2.PhysicalMax.Y );
+
+				pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X+changeLeft, pYAxis1.PhysicalMin.Y );
+				pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X+changeLeft, pYAxis1.PhysicalMax.Y );
+				pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X-changeRight, pYAxis2.PhysicalMin.Y );
+				pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X-changeRight, pYAxis2.PhysicalMax.Y );
+		
+			}
+		}
+
+
+		/// <summary>
+		/// Defines an AxisConstraint that forces the world length corresponding
+		/// to one pixel on the left y-axis to be a certain value. 
+		/// 
+		/// TODO: Allow the pixel world length to be set for the right axis.
+		/// </summary>
+		public class YPixelWorldLength : AxesConstraint
+		{
+			private double pWorldLength_ = 0.0;
+			private object holdFixedX_ = null;
+
+			/// <summary>
+			/// Constructor, which defines the world pixel length only. Both 
+			/// x-axes will be moved by equal amounts in order to force this
+			/// constraint.
+			/// </summary>
+			/// <param name="p">The world pixel length</param>
+			public YPixelWorldLength( double p )
+			{
+				this.pWorldLength_ = p;
+			}
+		
+			/// <summary>
+			/// Constructor, which defines the world pixel length together with
+			/// the x-axis that should be held constant when forcing this 
+			/// constraint [the other x-axis only will be moved].
+			/// </summary>
+			/// <param name="p">The world pixel length</param>
+			/// <param name="holdFixedX">The position of this x-axis will be held constant. The other x-axis will be moved in order to force the constraint.</param>
+			public YPixelWorldLength( double p, PlotSurface2D.XAxisPosition holdFixedX )
+			{
+				this.pWorldLength_ = p;
+				this.holdFixedX_ = holdFixedX;
+			}
+
+
+			/// <summary>
+			/// Applies the constraint to the axes.
+			/// </summary>
+			/// <param name="pXAxis1">The bottom x-axis.</param>
+			/// <param name="pYAxis1">The left y-axis.</param>
+			/// <param name="pXAxis2">The top x-axis.</param>
+			/// <param name="pYAxis2">The right y-axis.</param>
+			public override void ApplyConstraint( 
+				PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, 
+				PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 )
+			{
+
+				int desiredLength = (int)(pYAxis1.Axis.WorldLength / this.pWorldLength_);
+				int currentLength = pYAxis1.PhysicalLength;
+				int delta = currentLength - desiredLength;
+
+				int changeBottom = -delta / 2;
+				int changeTop = -delta / 2;
+				if (this.holdFixedX_ != null)
+				{
+					if ( (PlotSurface2D.XAxisPosition)this.holdFixedX_ == PlotSurface2D.XAxisPosition.Bottom )
+					{
+						changeBottom = 0;
+						changeTop = -delta;
+					}
+					else
+					{
+						changeBottom = -delta;
+						changeTop = 0;
+					}
+				}
+
+				pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X, pYAxis1.PhysicalMin.Y+changeBottom );
+				pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X, pYAxis1.PhysicalMax.Y-changeTop );
+				pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X, pYAxis2.PhysicalMin.Y+changeBottom );
+				pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X, pYAxis2.PhysicalMax.Y-changeTop );
+
+				pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMin.Y+changeBottom );
+				pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X, pXAxis1.PhysicalMax.Y+changeBottom );
+				pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X, pXAxis2.PhysicalMin.Y-changeTop );
+				pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X, pXAxis2.PhysicalMax.Y-changeTop );
+
+			}
+		}
+
+
+		/// <summary>
+		/// Defines an AxisConstraint that forces the specified axis to be placed at a 
+		/// specific physical position. The position of the axis opposite is held 
+		/// constant.
+		/// </summary>
+		public class AxisPosition : AxesConstraint
+		{
+
+			private object xAxisPosition_;
+			private object yAxisPosition_;
+			private int position_;
+
+			
+			/// <summary>
+			/// Constructor, which defines an horizontal axis and the physical
+			/// y position it should be drawn at.
+			/// </summary>
+			/// <param name="axis">The x-axis for which the y position is to be specified.</param>
+			/// <param name="yPosition">The [physical] y position of the axis.</param>
+			public AxisPosition( PlotSurface2D.XAxisPosition axis, int yPosition )
+			{
+				position_ = yPosition;
+				xAxisPosition_ = axis;
+			}
+
+
+			/// <summary>
+			/// Constructor, which defines a vertical axis and the physical
+			/// x position it should be drawn at.
+			/// </summary>
+			/// <param name="axis">The y-axis for which the x position is to be specified.</param>
+			/// <param name="xPosition">The [physical] x position of the axis.</param>
+			public AxisPosition( PlotSurface2D.YAxisPosition axis, int xPosition )
+			{
+				position_ = xPosition;
+				yAxisPosition_ = axis;
+			}
+
+			/// <summary>
+			/// Applies the constraint to the axes.
+			/// </summary>
+			/// <param name="pXAxis1">The bottom x-axis.</param>
+			/// <param name="pYAxis1">The left y-axis.</param>
+			/// <param name="pXAxis2">The top x-axis.</param>
+			/// <param name="pYAxis2">The right y-axis.</param>
+			public override void ApplyConstraint( 
+				PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, 
+				PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 )
+			{
+
+				if ( xAxisPosition_ != null )
+				{
+
+					if ((PlotSurface2D.XAxisPosition)xAxisPosition_ == PlotSurface2D.XAxisPosition.Bottom)
+					{
+						pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X, position_ );
+						pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X, position_ );
+			
+						pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X, position_ );
+						pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X, position_ );
+					}
+					else
+					{
+						pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X, position_ );
+						pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X, position_ );
+
+						pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X, position_ );
+						pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X, position_ );
+					}
+
+				}
+				else if (yAxisPosition_ != null )
+				{
+
+					if ((PlotSurface2D.YAxisPosition)yAxisPosition_ == PlotSurface2D.YAxisPosition.Left)
+					{
+						pYAxis1.PhysicalMin = new Point( position_, pYAxis1.PhysicalMin.Y );
+						pYAxis1.PhysicalMax = new Point( position_, pYAxis1.PhysicalMax.Y );
+
+						pXAxis1.PhysicalMin = new Point( position_, pXAxis1.PhysicalMin.Y );
+						pXAxis2.PhysicalMin = new Point( position_, pXAxis2.PhysicalMin.Y );
+					}
+					else
+					{
+						pYAxis2.PhysicalMin = new Point( position_, pYAxis2.PhysicalMin.Y );
+						pYAxis2.PhysicalMax = new Point( position_, pYAxis2.PhysicalMax.Y );
+				
+						pXAxis1.PhysicalMax = new Point( position_, pXAxis1.PhysicalMax.Y );
+						pXAxis2.PhysicalMax = new Point( position_, pXAxis2.PhysicalMax.Y );
+					}
+
+				}
+
+			}
+
+		}
+
+
+		/// <summary>
+		/// Defines an axes constraint that forces the world width and height pixel lengths
+		/// to be at the provided ratio. For example, an aspect ratio of 3:2 or
+		/// 1.5 indicates that there should be 1.5 times as many pixels per fixed
+		/// world length along the x direction than for the same world length along
+		/// the y direction. In other words, the world length of one pixel along 
+		/// the x direction is 2/3rds that of the world length of one pixel height
+		/// in the y direction.
+		/// </summary>
+		/// <remarks>
+		/// This class will never increase the size of the plot bounding box. It 
+		/// will always be made smaller.
+		/// </remarks>
+		public class AspectRatio : AxesConstraint
+		{
+			private double a_;
+			private object holdFixedX_ = null;
+			private object holdFixedY_ = null;
+
+			/// <summary>
+			/// Constructor.
+			/// </summary>
+			/// <param name="a">Aspect Ratio</param>
+			public AspectRatio( double a )
+			{
+				this.a_ = a;
+			}
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="a">Aspect Ratio</param>
+			/// <param name="holdFixedX">
+			/// When adjusting the position of axes, the specified axis will never
+			/// be moved.
+			/// </param>
+			public AspectRatio( double a, PlotSurface2D.XAxisPosition holdFixedX )
+			{
+				this.a_ = a;
+				this.holdFixedX_ = holdFixedX;
+			}
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="a">Aspect Ratio</param>
+			/// <param name="holdFixedY">
+			/// When adjusting the position of axes, the 
+			/// specified axis will never be moved.
+			/// </param>
+			public AspectRatio( double a, PlotSurface2D.YAxisPosition holdFixedY )
+			{
+				this.a_ = a;
+				this.holdFixedY_ = holdFixedY;
+			}
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="a">Aspect Ratio</param>
+			/// <param name="holdFixedX">When adjusting the position of axes, the specified axis will never be moved.</param>
+			/// <param name="holdFixedY">When adjusting the position of axes, the specified axis will never be moved.</param>
+			public AspectRatio( 
+				double a,
+				PlotSurface2D.XAxisPosition holdFixedX, 
+				PlotSurface2D.YAxisPosition holdFixedY )
+			{
+				this.a_ = a;
+				this.holdFixedX_ = holdFixedX;
+				this.holdFixedY_ = holdFixedY;
+			}
+							
+			/// <summary>
+			/// Applies the constraint to the axes.
+			/// </summary>
+			/// <param name="pXAxis1">The bottom x-axis.</param>
+			/// <param name="pYAxis1">The left y-axis.</param>
+			/// <param name="pXAxis2">The top x-axis.</param>
+			/// <param name="pYAxis2">The right y-axis.</param>
+			public override void ApplyConstraint( 
+				PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, 
+				PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 )
+			{
+				double xWorldRange = Math.Abs( pXAxis1.Axis.WorldMax - pXAxis1.Axis.WorldMin );
+				double xPhysicalRange = Math.Abs( pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X );
+				double xDirPixelSize =  xWorldRange / xPhysicalRange;
+			
+				double yWorldRange = Math.Abs( pYAxis1.Axis.WorldMax - pYAxis1.Axis.WorldMin );
+				double yPhysicalRange = Math.Abs( pYAxis1.PhysicalMax.Y - pYAxis1.PhysicalMin.Y );
+				double yDirPixelSize =  yWorldRange / yPhysicalRange;
+
+				double currentAspectRatio = yDirPixelSize / xDirPixelSize;
+
+				// we want to change the current aspect ratio to be the desired.
+				// to do this, we may only add the world pixel lengths.
+
+				if ( this.a_ > currentAspectRatio )
+				{
+					// want to increase aspect ratio. Therefore, want to add some amount
+					// to yDirPixelSize (numerator).
+
+					double toAdd = ( this.a_ - currentAspectRatio ) * xDirPixelSize;
+					int newHeight =
+						(int)( Math.Abs(pYAxis1.Axis.WorldMax - pYAxis1.Axis.WorldMin) / (yDirPixelSize + toAdd) );
+					int changeInHeight = (int)yPhysicalRange - newHeight;
+
+					int changeBottom = changeInHeight/2;
+					int changeTop = changeInHeight/2;
+					if (this.holdFixedX_ != null)
+					{
+						if ( (PlotSurface2D.XAxisPosition)this.holdFixedX_ == PlotSurface2D.XAxisPosition.Bottom )
+						{
+							changeBottom = 0;
+							changeTop = changeInHeight;
+						}
+						else
+						{
+							changeBottom = changeInHeight;
+							changeTop = 0;
+						}
+					}
+
+					pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X, pYAxis1.PhysicalMin.Y-changeBottom );
+					pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X, pYAxis1.PhysicalMax.Y+changeTop );
+					pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X, pYAxis2.PhysicalMin.Y-changeBottom );
+					pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X, pYAxis2.PhysicalMax.Y+changeTop );
+
+					pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMin.Y-changeBottom );
+					pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X, pXAxis1.PhysicalMax.Y-changeBottom );
+					pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X, pXAxis2.PhysicalMin.Y+changeTop );
+					pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X, pXAxis2.PhysicalMax.Y+changeTop );
+
+				}
+
+				else 
+				{
+
+					// want to decrease aspect ratio. Therefore, want to add some amount
+					// to xDirPixelSize (denominator).
+
+					double toAdd = yDirPixelSize / this.a_ - xDirPixelSize;
+					int newWidth = 
+						(int)( Math.Abs(pXAxis1.Axis.WorldMax - pXAxis1.Axis.WorldMin) / (xDirPixelSize + toAdd) );
+					int changeInWidth = (int)xPhysicalRange - newWidth;
+
+					int changeLeft = changeInWidth / 2;
+					int changeRight = changeInWidth / 2;
+					if (this.holdFixedY_ != null)
+					{
+						if ( (PlotSurface2D.YAxisPosition)this.holdFixedY_ == PlotSurface2D.YAxisPosition.Left )
+						{
+							changeLeft = 0;
+							changeRight = changeInWidth;
+						}
+						else
+						{
+							changeLeft = changeInWidth;
+							changeRight = 0;
+						}
+					}
+
+					pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X+changeLeft, pXAxis1.PhysicalMin.Y );
+					pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X-changeRight, pXAxis1.PhysicalMax.Y );
+					pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X+changeLeft, pXAxis2.PhysicalMin.Y );
+					pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X-changeRight, pXAxis2.PhysicalMax.Y );
+
+					pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X+changeLeft, pYAxis1.PhysicalMin.Y );
+					pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X+changeLeft, pYAxis1.PhysicalMax.Y );
+					pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X-changeRight, pYAxis2.PhysicalMin.Y );
+					pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X-changeRight, pYAxis2.PhysicalMax.Y );
+
+				}
+
+			}
+
+		}
+		
+
+		/// <summary>
+		/// Applies the constraint to the axes. Must be overriden.
+		/// </summary>
+		/// <param name="pXAxis1">The bottom x-axis.</param>
+		/// <param name="pYAxis1">The left y-axis.</param>
+		/// <param name="pXAxis2">The top x-axis.</param>
+		/// <param name="pYAxis2">The right y-axis.</param>
+		public abstract void ApplyConstraint( 
+			PhysicalAxis pXAxis1, PhysicalAxis pYAxis1, 
+			PhysicalAxis pXAxis2, PhysicalAxis pYAxis2 );
+	}
+
+}
diff --git a/nplot/nplot/Axis.cs b/nplot/nplot/Axis.cs
new file mode 100644
index 0000000..56e2885
--- /dev/null
+++ b/nplot/nplot/Axis.cs
@@ -0,0 +1,1654 @@
+/*
+NPlot - A charting library for .NET
+
+Axis.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Drawing.Drawing2D;
+using System.Drawing;
+using System;
+using System.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Encapsulates functionality common to all axis classes. All specific
+	/// axis classes derive from Axis. Axis can be used as a concrete class
+	/// itself - it is an Axis without any embilishments [tick marks or tick
+	/// mark labels].<br></br><br></br>
+	/// This class encapsulates no physical information about where the axes
+	/// are drawn. 
+	/// </summary>
+	public class Axis : System.ICloneable
+	{
+	
+		/// <summary>
+		/// If true, tick marks will cross the axis, with their centre on the axis line.
+		/// If false, tick marks will be drawn as a line with origin starting on the axis line.
+		/// </summary>
+		public bool TicksCrossAxis
+		{
+			get
+			{
+				return ticksCrossAxis_;
+			}
+			set
+			{
+				ticksCrossAxis_ = value;
+			}
+		}
+		bool ticksCrossAxis_ = false;
+
+
+		/// <summary>
+		/// The maximum world extent of the axis. Note that it is sensical if 
+		/// WorldMax is less than WorldMin - the axis would just be descending
+		/// not ascending. Currently Axes won't display properly if you do 
+		/// this - use the Axis.Reversed property instead to achieve the same
+		/// result.
+		/// 
+        /// Setting this raises the WorldMinChanged event and the WorldExtentsChanged event.
+        /// </summary>
+		public virtual double WorldMax
+		{
+			get
+			{
+				return worldMax_;
+			}
+			set
+			{
+				this.worldMax_ = value;
+                /*
+                if (this.WorldExtentsChanged != null)
+                    this.WorldExtentsChanged(this, new WorldValueChangedArgs(worldMax_, WorldValueChangedArgs.MinMaxType.Max));
+                if (this.WorldMaxChanged != null)
+                    this.WorldMaxChanged(this, new WorldValueChangedArgs(worldMax_, WorldValueChangedArgs.MinMaxType.Max));
+                */
+            }
+		}
+		private double worldMax_;
+		
+
+		/// <summary>
+		/// The minumum world extent of the axis. Note that it is sensical if 
+		/// WorldMax is less than WorldMin - the axis would just be descending
+		/// not ascending. Currently Axes won't display properly if you do 
+		/// this - use the Axis.Reversed property instead to achieve the same
+		/// result.
+		/// 
+		/// Setting this raises the WorldMinChanged event and the WorldExtentsChanged event.
+		/// </summary>
+		public virtual double WorldMin
+		{
+			get
+			{
+				return this.worldMin_;
+			}
+			set
+			{
+				this.worldMin_ = value;
+                /*
+                if (this.WorldExtentsChanged != null)
+                    this.WorldExtentsChanged( this, new WorldValueChangedArgs( worldMin_, WorldValueChangedArgs.MinMaxType.Min) );
+                if (this.WorldMinChanged != null)
+                    this.WorldMinChanged( this, new WorldValueChangedArgs(worldMin_, WorldValueChangedArgs.MinMaxType.Min) );
+                */
+            }
+		}
+		private double worldMin_;
+
+
+		/// <summary>
+		/// Length (in pixels) of a large tick. <b>Not</b> the distance 
+		/// between large ticks. The length of the tick itself.
+		/// </summary>
+		public int LargeTickSize
+		{
+			get
+			{
+				return largeTickSize_;
+			}
+			set
+			{
+				largeTickSize_ = value;
+			}
+		}
+		private int largeTickSize_;
+
+
+		/// <summary>
+		/// Length (in pixels) of the small ticks.
+		/// </summary>
+		public int SmallTickSize
+		{
+			get
+			{
+				return smallTickSize_;
+			}
+			set
+			{
+				smallTickSize_ = value;
+			}
+		}
+		private int smallTickSize_;
+
+
+		/// <summary>
+		/// The Axis Label
+		/// </summary>
+		public string Label
+		{
+			get
+			{
+				return label_;
+			}
+			set
+			{
+				label_ = value;
+			}
+		}
+		private string label_;
+
+
+		/// <summary>
+		/// If true, text associated with tick marks will be drawn on the other side of the
+		/// axis line [next to the axis]. If false, tick mark text will be drawn at the end
+		/// of the tick mark [on the same of the axis line as the tick].
+		/// </summary>
+		public bool TickTextNextToAxis
+		{
+			get
+			{
+				return tickTextNextToAxis_;
+			}
+			set
+			{
+				tickTextNextToAxis_ = value;
+			}
+		}
+		bool tickTextNextToAxis_;
+
+
+		/// <summary>
+		/// If set to true, the axis is hidden. That is, the axis line, ticks, tick 
+		/// labels and axis label will not be drawn. 
+		/// </summary>
+		public bool Hidden
+		{
+			get
+			{
+				return hidden_;
+			}
+			set
+			{
+				hidden_ = value;
+			}
+		}
+		private bool hidden_;
+
+
+		/// <summary>
+		/// If set true, the axis will behave as though the WorldMin and WorldMax values
+		/// have been swapped.
+		/// </summary>
+		public bool Reversed
+		{
+			get
+			{
+				return reversed_;
+			}
+			set
+			{
+				reversed_ = value;
+			}
+		}
+		private bool reversed_;
+
+
+		/// <summary>
+		/// If true, no text will be drawn next to any axis tick marks.
+		/// </summary>
+		public bool HideTickText
+		{
+			get
+			{
+				return hideTickText_;
+			}
+			set
+			{
+				hideTickText_ = value;
+			}
+		}
+		private bool hideTickText_;
+
+
+		/// <summary>
+		/// This font is used for the drawing of text next to the axis tick marks.
+		/// </summary>
+		public Font TickTextFont
+		{
+			get
+			{
+				return this.tickTextFont_;
+			}
+			set
+			{
+				this.tickTextFont_ = value;
+				UpdateScale();
+			}
+		}
+		private Font tickTextFont_;
+		private Font tickTextFontScaled_;
+
+
+		/// <summary>
+		/// This font is used to draw the axis label.
+		/// </summary>
+		public Font LabelFont
+		{
+			get
+			{
+				return labelFont_;
+			}
+			set
+			{
+				labelFont_ = value;
+				UpdateScale();
+			}
+		}
+		private Font labelFont_;
+		private Font labelFontScaled_;
+
+
+		/// <summary>
+		/// Specifies the format used for drawing tick labels. See 
+		/// StringBuilder.AppendFormat for a description of this 
+		/// string.
+		/// </summary>
+		public string NumberFormat
+		{
+			get
+			{
+				return numberFormat_;
+			}
+			set
+			{
+				numberFormat_ = value;
+			}
+		}
+		private string numberFormat_;
+
+
+		/// <summary>
+		/// If LargeTickStep isn't specified, then this will be calculated 
+		/// automatically. The calculated value will not be less than this
+		/// amount.
+		/// </summary>
+		public int MinPhysicalLargeTickStep
+		{
+			get
+			{
+				return minPhysicalLargeTickStep_;
+			}
+			set
+			{
+				minPhysicalLargeTickStep_ = value;
+			}
+		}
+		private int minPhysicalLargeTickStep_ = 30;
+
+
+		/// <summary>
+		/// The color of the pen used to draw the ticks and the axis line.
+		/// </summary>
+		public System.Drawing.Color AxisColor
+		{
+			get
+			{
+				return linePen_.Color;
+			}
+			set
+			{
+				linePen_ = new Pen( (Color)value );
+			}
+		}
+
+
+		/// <summary>
+		/// The pen used to draw the ticks and the axis line.
+		/// </summary>
+		public System.Drawing.Pen AxisPen
+		{
+			get
+			{
+				return linePen_;
+			}
+			set
+			{
+				linePen_ = value;
+			}
+		}
+		private System.Drawing.Pen linePen_;
+
+
+		/// <summary>
+		/// If true, automated tick placement will be independent of the physical
+		/// extent of the axis. Tick placement will look good for charts of typical
+		/// size (say physical dimensions of 640x480). If you want to produce the
+		/// same chart on two graphics surfaces of different sizes [eg Windows.Forms
+		/// control and printer], then you will want to set this property to true.
+		/// If false [default], the number of ticks and their placement will be 
+		/// optimally calculated to look the best for the given axis extent. This 
+		/// is very useful if you are creating a cart with particularly small or
+		/// large physical dimensions.
+		/// </summary>
+		public bool TicksIndependentOfPhysicalExtent
+		{
+			get
+			{
+				return ticksIndependentOfPhysicalExtent_;
+			}
+			set
+			{
+				ticksIndependentOfPhysicalExtent_ = value;
+			}
+		}
+		private bool ticksIndependentOfPhysicalExtent_ = false;
+
+
+		/// <summary>
+		/// If true label is flipped about the text center line parallel to the text.
+		/// </summary>
+		public bool FlipTicksLabel
+		{
+			get 
+			{
+				return flipTicksLabel_; 
+			}
+			set 
+			{ 
+				flipTicksLabel_ = value; 
+			}
+		}
+ 		private bool flipTicksLabel_ = false;
+
+
+		/// <summary>
+		/// Angle to draw ticks at (measured anti-clockwise from axis direction).
+		/// </summary>
+		public float TicksAngle
+		{
+			get
+			{
+				return ticksAngle_;
+			}
+			set
+			{
+				ticksAngle_ = value;
+			}
+		}
+		private float ticksAngle_ = (float)Math.PI / 2.0f;
+
+
+		/// <summary>
+		/// Angle to draw large tick labels at (clockwise from horizontal). Note: 
+		/// this is currently only implemented well for the lower x-axis. 
+		/// </summary>
+		public float TicksLabelAngle
+		{
+			get
+			{
+				return ticksLabelAngle_;
+			}
+			set
+			{
+				ticksLabelAngle_ = value;
+			}
+		}
+		private float ticksLabelAngle_ = 0.0f;
+
+
+		/// <summary>
+		/// The color of the brush used to draw the axis label.
+		/// </summary>
+		public Color LabelColor
+		{
+			set
+			{
+				labelBrush_ = new SolidBrush( value );
+			}
+		}
+		
+		
+		/// <summary>
+		/// The brush used to draw the axis label.
+		/// </summary>
+		public Brush LabelBrush
+		{
+			get
+			{
+				return labelBrush_;
+			}
+			set
+			{
+				labelBrush_ = value;
+			}
+		}
+		private Brush labelBrush_;
+
+		
+		/// <summary>
+		/// The color of the brush used to draw the axis tick labels.
+		/// </summary>
+		public Color TickTextColor
+		{
+			set
+			{
+				tickTextBrush_ = new SolidBrush( value );
+			}
+		}
+
+
+		/// <summary>
+		/// The brush used to draw the tick text.
+		/// </summary>
+		public Brush TickTextBrush
+		{
+			get
+			{
+				return tickTextBrush_;
+			}
+			set
+			{
+				tickTextBrush_ = value;
+			}
+		}
+		private Brush tickTextBrush_;
+
+
+		/// <summary>
+		/// If true, label and tick text will be scaled to match size
+		/// of PlotSurface2D. If false, they won't be.
+		/// </summary>
+		/// <remarks>Could also be argued this belongs in PlotSurface2D</remarks>
+		public bool AutoScaleText
+		{
+			get
+			{
+				return autoScaleText_;
+			}
+			set
+			{
+				autoScaleText_ = value;
+			}
+		}
+		private bool autoScaleText_;
+
+
+		/// <summary>
+		/// If true, tick lengths will be scaled to match size
+		/// of PlotSurface2D. If false, they won't be.
+		/// </summary>
+		/// <remarks>Could also be argued this belongs in PlotSurface2D</remarks>
+		public bool AutoScaleTicks
+		{
+			get
+			{
+				return autoScaleTicks_;
+			}
+			set
+			{
+				autoScaleTicks_ = value;
+			}
+		}
+		private bool autoScaleTicks_;
+
+
+		/// <summary>
+		/// Deep copy of Axis.
+		/// </summary>
+		/// <remarks>
+		/// This method includes a check that guards against derived classes forgetting
+		/// to implement their own Clone method. If Clone is called on a object derived
+		/// from Axis, and the Clone method hasn't been overridden by that object, then
+		/// the test this.GetType == typeof(Axis) will fail.
+		/// </remarks>
+		/// <returns>A copy of the Axis Class</returns>
+		public virtual object Clone()
+		{
+			// ensure that this isn't being called on a derived type. If that is the case
+			// then the derived type didn't override this method as it should have.
+			if (this.GetType() != typeof(Axis))
+			{
+				throw new NPlotException( "Clone not defined in derived type." );
+			}
+			
+			Axis a = new Axis();
+			DoClone( this, a );
+			return a;
+		}
+
+
+		/// <summary>
+		/// Helper method for Clone. Does all the copying - can be called by derived
+		/// types so they don't need to implement this part of the copying themselves.
+		/// also useful in constructor of derived types that takes Axis class.
+		/// </summary>
+		protected static void DoClone( Axis b, Axis a )
+		{
+
+			// value items
+			a.autoScaleText_ = b.autoScaleText_;
+			a.autoScaleTicks_ = b.autoScaleTicks_;
+			a.worldMax_ = b.worldMax_;
+			a.worldMin_ = b.worldMin_;
+			a.tickTextNextToAxis_ = b.tickTextNextToAxis_;
+			a.hidden_ = b.hidden_;
+			a.hideTickText_ = b.hideTickText_;
+			a.reversed_ = b.reversed_;
+			a.ticksAngle_ = b.ticksAngle_;
+			a.ticksLabelAngle_ = b.ticksLabelAngle_;
+			a.minPhysicalLargeTickStep_ = b.minPhysicalLargeTickStep_;
+			a.ticksIndependentOfPhysicalExtent_ = b.ticksIndependentOfPhysicalExtent_;
+			a.largeTickSize_ = b.largeTickSize_;
+			a.smallTickSize_ = b.smallTickSize_;
+			a.ticksCrossAxis_ = b.ticksCrossAxis_;
+			a.labelOffset_ = b.labelOffset_;
+            a.labelOffsetAbsolute_ = b.labelOffsetAbsolute_;
+            a.labelOffsetScaled_ = b.labelOffsetScaled_;
+
+			// reference items.
+			a.tickTextFont_ = (Font)b.tickTextFont_.Clone();
+			a.label_ = (string)b.label_.Clone();
+			if (b.numberFormat_ != null) 
+			{
+				a.numberFormat_ = (string)b.numberFormat_.Clone();
+			}
+			else
+			{
+				a.numberFormat_ = null;
+			}
+
+			a.labelFont_ = (Font)b.labelFont_.Clone();
+			a.linePen_ = (Pen)b.linePen_.Clone();
+			a.tickTextBrush_ = (Brush)b.tickTextBrush_.Clone();
+			a.labelBrush_ = (Brush)b.labelBrush_.Clone();
+
+			a.FontScale = b.FontScale;
+			a.TickScale = b.TickScale;
+
+		}
+
+
+
+		/// <summary>
+		/// Helper function for constructors.
+		/// Do initialization here so that Clear() method is handled properly
+		/// </summary>
+		private void Init()
+		{
+			this.worldMax_ = double.NaN;
+			this.worldMin_ = double.NaN;
+			this.Hidden = false;
+			this.SmallTickSize = 2;
+			this.LargeTickSize = 6;
+			this.FontScale = 1.0f;
+			this.TickScale = 1.0f;
+			this.AutoScaleTicks = false;
+			this.AutoScaleText = false;
+			this.TickTextNextToAxis = true;
+			this.HideTickText = false;
+			this.TicksCrossAxis = false;
+			this.LabelOffset = 0.0f;
+            this.LabelOffsetAbsolute = false;
+            this.LabelOffsetScaled = true;
+
+			this.Label = "" ;
+			this.NumberFormat = null;
+			this.Reversed = false;
+
+			FontFamily fontFamily = new FontFamily( "Arial" );
+			this.TickTextFont = new Font( fontFamily, 10, FontStyle.Regular, GraphicsUnit.Pixel );
+			this.LabelFont = new Font( fontFamily, 12, FontStyle.Regular, GraphicsUnit.Pixel );
+			this.LabelColor = System.Drawing.Color.Black;
+			this.TickTextColor = System.Drawing.Color.Black;
+			this.linePen_ = new Pen( System.Drawing.Color.Black );
+			this.linePen_.Width = 1.0f;
+			this.FontScale = 1.0f;
+
+			// saves constructing these in draw method.
+			drawFormat_ = new StringFormat();
+			drawFormat_.Alignment = StringAlignment.Center;
+		}
+
+		StringFormat drawFormat_;
+
+
+		/// <summary>
+		/// Default constructor
+		/// </summary>
+		public Axis( )
+		{
+			this.Init();
+		}
+
+
+		/// <summary>
+		/// Constructor that takes only world min and max values.
+		/// </summary>
+		/// <param name="worldMin">The minimum world coordinate.</param>
+		/// <param name="worldMax">The maximum world coordinate.</param>
+		public Axis( double worldMin, double worldMax )
+		{
+			this.Init();
+			this.WorldMin = worldMin;
+			this.WorldMax = worldMax;
+		}
+
+
+		/// <summary>
+		/// Copy constructor.
+		/// </summary>
+		/// <param name="a">The Axis to clone.</param>
+		public Axis( Axis a )
+		{
+			Axis.DoClone( a, this );
+		}
+
+
+		/// <summary>
+		/// Determines whether a world value is outside range WorldMin -> WorldMax
+		/// </summary>
+		/// <param name="coord">the world value to test</param>
+		/// <returns>true if outside limits, false otherwise</returns>
+		public bool OutOfRange( double coord )
+		{
+			if (double.IsNaN(WorldMin) || double.IsNaN(WorldMax))
+			{
+				throw new NPlotException( "world min / max not set" );
+			}
+
+			if (coord > this.WorldMax || coord < this.WorldMin)
+			{
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+
+		/// <summary>
+		/// Sets the world extent of the current axis to be just large enough
+		/// to encompas the current world extent of the axis, and the world
+		/// extent of the passed in axis
+		/// </summary>
+		/// <param name="a">The other Axis instance.</param>
+		public void LUB( Axis a )
+		{
+			if (a == null)
+			{
+				return;
+			}
+
+			// mins
+			if (!double.IsNaN(a.worldMin_))
+			{
+				if (double.IsNaN(worldMin_))
+				{
+					WorldMin = a.WorldMin;
+				}
+				else
+				{
+					if (a.WorldMin < WorldMin)
+					{
+						WorldMin = a.WorldMin;
+					}
+				}
+			}
+
+			// maxs.
+			if (!double.IsNaN(a.worldMax_))
+			{
+				if (double.IsNaN(worldMax_))
+				{
+					WorldMax = a.WorldMax;
+				}
+				else
+				{
+					if (a.WorldMax > WorldMax)
+					{
+						WorldMax = a.WorldMax;
+					}
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// World to physical coordinate transform.
+		/// </summary>
+		/// <param name="coord">The coordinate value to transform.</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="clip">if false, then physical value may extend outside worldMin / worldMax. If true, the physical value returned will be clipped to physicalMin or physicalMax if it lies outside this range.</param>
+		/// <returns>The transformed coordinates.</returns>
+		/// <remarks>Not sure how much time is spent in this often called function. If it's lots, then
+		/// worth optimizing (there is scope to do so).</remarks>
+		public virtual PointF WorldToPhysical( 
+			double coord, 
+			PointF physicalMin, 
+			PointF physicalMax, 
+			bool clip )
+		{
+
+			// (1) account for reversed axis. Could be tricky and move
+			// this out, but would be a little messy.
+
+			PointF _physicalMin;
+			PointF _physicalMax;
+
+			if ( this.Reversed )
+			{
+				_physicalMin = physicalMax;
+				_physicalMax = physicalMin;
+			}
+			else
+			{
+				_physicalMin = physicalMin;
+				_physicalMax = physicalMax;
+			}
+
+
+			// (2) if want clipped value, return extrema if outside range.
+			
+			if ( clip )
+			{
+				if ( WorldMin < WorldMax )
+				{
+					if ( coord > WorldMax )
+					{
+						return _physicalMax;
+					}
+					if ( coord < WorldMin )
+					{
+						return _physicalMin;
+					}
+				}
+				else
+				{
+					if ( coord < WorldMax )
+					{
+						return _physicalMax;
+					}
+					if ( coord > WorldMin )
+					{
+						return _physicalMin;
+					}
+				}
+			}
+
+
+			// (3) we are inside range or don't want to clip.
+
+			double range = WorldMax - WorldMin;
+			double prop = (double)((coord - WorldMin) / range);
+
+			// Force clipping at bounding box largeClip times that of real bounding box 
+			// anyway. This is effectively at infinity.
+			const double largeClip = 100.0;
+			if (prop > largeClip && clip)
+				prop = largeClip;
+
+			if (prop < -largeClip && clip)
+				prop = -largeClip;
+
+			if (range == 0)
+			{
+				if (coord >= WorldMin)
+					prop = largeClip;
+
+				if (coord < WorldMin)
+					prop = -largeClip;
+			}
+
+			// calculate the physical coordinate.
+			PointF offset = new PointF( 
+				(float)(prop * (_physicalMax.X - _physicalMin.X)),
+				(float)(prop * (_physicalMax.Y - _physicalMin.Y)) );
+
+			return new PointF( (int)(_physicalMin.X + offset.X), (int)(_physicalMin.Y + offset.Y) );
+		}
+
+
+		/// <summary>
+		/// Return the world coordinate of the projection of the point p onto
+		/// the axis.
+		/// </summary>
+		/// <param name="p">The point to project onto the axis</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="clip">If true, the world value will be clipped to WorldMin or WorldMax as appropriate if it lies outside this range.</param>
+		/// <returns>The world value corresponding to the projection of the point p onto the axis.</returns>
+		public virtual double PhysicalToWorld( 
+			PointF p, 
+			PointF physicalMin, 
+			PointF physicalMax,
+			bool clip )
+		{
+
+			// (1) account for reversed axis. Could be tricky and move
+			// this out, but would be a little messy.
+
+			PointF _physicalMin;
+			PointF _physicalMax;
+
+			if ( this.Reversed )
+			{
+				_physicalMin = physicalMax;
+				_physicalMax = physicalMin;
+			}
+			else
+			{
+				_physicalMin = physicalMin;
+				_physicalMax = physicalMax;
+			}
+
+			// normalised axis dir vector
+			float axis_X = _physicalMax.X - _physicalMin.X;
+			float axis_Y = _physicalMax.Y - _physicalMin.Y;
+			float len = (float)Math.Sqrt( axis_X * axis_X + axis_Y * axis_Y );
+			axis_X /= len;
+			axis_Y /= len;
+
+			// point relative to axis physical minimum.
+			PointF posRel = new PointF( p.X - _physicalMin.X, p.Y - _physicalMin.Y );
+
+			// dist of point projection on axis, normalised.
+			float prop = ( axis_X * posRel.X + axis_Y * posRel.Y ) / len;
+
+			double world = prop * (this.WorldMax - this.WorldMin) + this.WorldMin;
+
+			// if want clipped value, return extrema if outside range.
+			if (clip)
+			{
+				world = Math.Max( world, this.WorldMin );
+				world = Math.Min( world, this.WorldMax );
+			}
+
+			return world;
+		}
+
+
+		/// <summary>
+		/// Draw the Axis Label
+		/// </summary>
+		/// <param name="g">The GDI+ drawing surface on which to draw.</param>
+		/// <param name="offset">offset from axis. Should be calculated so as to make sure axis label misses tick labels.</param>
+		/// <param name="axisPhysicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="axisPhysicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <returns>boxed Rectangle indicating bounding box of label. null if no label printed.</returns>
+		public object DrawLabel( 
+			Graphics g, 
+			Point offset, 
+			Point axisPhysicalMin, 
+			Point axisPhysicalMax )
+		{
+
+			if ( Label != "" )
+			{
+	
+				// first calculate any extra offset for axis label spacing.
+				float extraOffsetAmount = this.LabelOffset;
+				extraOffsetAmount += 2.0f; // empirically determed - text was too close to axis before this.
+				
+				if (this.AutoScaleText)
+				{
+					if (this.LabelOffsetScaled)
+					{
+						extraOffsetAmount *= this.FontScale;
+					}
+				}
+
+				// now extend offset.
+				float offsetLength = (float)Math.Sqrt( offset.X*offset.X + offset.Y*offset.Y );
+				if (offsetLength > 0.01)
+				{
+					float x_component = offset.X / offsetLength;
+					float y_component = offset.Y / offsetLength;
+
+					x_component *= extraOffsetAmount;
+					y_component *= extraOffsetAmount;
+
+                    if (this.LabelOffsetAbsolute)
+                    {
+                        offset.X = (int)x_component;
+                        offset.Y = (int)y_component;
+                    }
+                    else
+                    {
+                        offset.X += (int)x_component;
+                        offset.Y += (int)y_component;
+                    }
+                }
+				
+				// determine angle of axis in degrees
+				double theta = Math.Atan2(
+					axisPhysicalMax.Y - axisPhysicalMin.Y,
+					axisPhysicalMax.X - axisPhysicalMin.X );
+				theta = theta * 180.0f / Math.PI;
+
+				PointF average = new PointF(
+					(axisPhysicalMax.X + axisPhysicalMin.X)/2.0f,
+					(axisPhysicalMax.Y + axisPhysicalMin.Y)/2.0f );
+
+				g.TranslateTransform( offset.X , offset.Y );	// this is done last.
+				g.TranslateTransform( average.X, average.Y );
+				g.RotateTransform( (float)theta );				// this is done first.
+
+				SizeF labelSize = g.MeasureString( Label, labelFontScaled_);
+
+				//bounding box for label centered around zero.
+				RectangleF drawRect = new RectangleF( 
+					-labelSize.Width/2.0f,
+					-labelSize.Height/2.0f,
+					labelSize.Width,
+					labelSize.Height );
+				
+				g.DrawString( 
+					Label, 
+					labelFontScaled_,
+					labelBrush_, 
+					drawRect,
+					drawFormat_ );
+
+				// now work out physical bounds of label. 
+				Matrix m = g.Transform;
+				PointF[] recPoints = new PointF[2];
+				recPoints[0] = new PointF( -labelSize.Width/2.0f, -labelSize.Height/2.0f );
+				recPoints[1] = new PointF( labelSize.Width/2.0f, labelSize.Height/2.0f );
+				m.TransformPoints( recPoints );
+
+				int x1 = (int)Math.Min( recPoints[0].X, recPoints[1].X );
+				int x2 = (int)Math.Max( recPoints[0].X, recPoints[1].X );
+				int y1 = (int)Math.Min( recPoints[0].Y, recPoints[1].Y );
+				int y2 = (int)Math.Max( recPoints[0].Y, recPoints[1].Y );
+
+				g.ResetTransform();
+
+				// and return label bounding box.
+				return new Rectangle( x1, y1, (x2-x1), (y2-y1) );
+			}
+
+			return null;
+		}
+
+
+		/// <summary>
+		/// Draw a tick on the axis.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="w">The tick position in world coordinates.</param>
+		/// <param name="size">The size of the tick (in pixels)</param>
+		/// <param name="text">The text associated with the tick</param>
+		/// <param name="textOffset">The Offset to draw from the auto calculated position</param>
+		/// <param name="axisPhysMin">The minimum physical extent of the axis</param>
+		/// <param name="axisPhysMax">The maximum physical extent of the axis</param>
+		/// <param name="boundingBox">out: The bounding rectangle for the tick and tickLabel drawn</param>
+		/// <param name="labelOffset">out: offset from the axies required for axis label</param>
+		public virtual void DrawTick( 
+			Graphics g, 
+			double w,
+			float size,
+			string text,
+			Point textOffset,
+			Point axisPhysMin,
+			Point axisPhysMax,
+			out Point labelOffset,
+			out Rectangle boundingBox )
+		{
+
+			// determine physical location where tick touches axis. 
+			PointF tickStart = WorldToPhysical( w, axisPhysMin, axisPhysMax, true );
+
+			// determine offset from start point.
+			PointF axisDir = Utils.UnitVector( axisPhysMin, axisPhysMax );
+
+			// rotate axis dir clockwise by angle radians to get tick direction.
+			float x1 = (float)(Math.Cos( -this.TicksAngle ) * axisDir.X + Math.Sin( -this.TicksAngle ) * axisDir.Y);
+			float y1 = (float)(-Math.Sin( -this.TicksAngle ) * axisDir.X + Math.Cos( -this.TicksAngle ) * axisDir.Y);
+
+			// now get the scaled tick vector.
+			PointF tickVector = new PointF( this.TickScale * size * x1, this.TickScale * size * y1 );
+
+			if (this.TicksCrossAxis)
+			{
+				tickStart = new PointF(
+					tickStart.X - tickVector.X / 2.0f,
+					tickStart.Y - tickVector.Y / 2.0f );
+			}
+
+			// and the end point [point off axis] of tick mark.
+			PointF tickEnd = new PointF( tickStart.X + tickVector.X, tickStart.Y + tickVector.Y );
+
+			// and draw it!
+			if (g != null)
+				g.DrawLine( this.linePen_, (int)tickStart.X, (int)tickStart.Y, (int)tickEnd.X, (int)tickEnd.Y );
+			// note: casting to int for tick positions was necessary to ensure ticks drawn where we wanted
+			// them. Not sure of the reason.
+
+			// calculate bounds of tick.
+			int minX = (int)Math.Min( tickStart.X, tickEnd.X );
+			int minY = (int)Math.Min( tickStart.Y, tickEnd.Y );
+			int maxX = (int)Math.Max( tickStart.X, tickEnd.X );
+			int maxY = (int)Math.Max( tickStart.Y, tickEnd.Y );
+			boundingBox = new Rectangle( minX, minY, maxX-minX, maxY-minY );
+			
+			// by default, label offset from axis is 0. TODO: revise this.
+			labelOffset = new Point( 
+				-(int)tickVector.X, 
+				-(int)tickVector.Y );
+
+			// ------------------------
+
+			// now draw associated text.
+
+			// **** TODO ****
+			// The following code needs revising. A few things are hard coded when
+			// they should not be. Also, angled tick text currently just works for
+			// the bottom x-axis. Also, it's a bit hacky.
+
+			if (text != "" && !HideTickText && g != null )
+			{
+				SizeF textSize = g.MeasureString( text, tickTextFontScaled_ );
+
+				// determine the center point of the tick text.
+				float textCenterX;
+				float textCenterY;
+
+				// if text is at pointy end of tick.
+				if (!this.TickTextNextToAxis)
+				{
+					// offset due to tick.
+					textCenterX = tickStart.X + tickVector.X*1.2f;
+					textCenterY = tickStart.Y + tickVector.Y*1.2f;
+
+					// offset due to text box size.
+					textCenterX += 0.5f * x1 * textSize.Width;
+					textCenterY += 0.5f * y1 * textSize.Height;
+				}
+					// else it's next to the axis.
+				else
+				{
+					// start location.
+					textCenterX = tickStart.X;
+					textCenterY = tickStart.Y;
+
+					// offset due to text box size.
+					textCenterX -= 0.5f * x1 * textSize.Width;
+					textCenterY -= 0.5f * y1 * textSize.Height;
+
+					// bring text away from the axis a little bit.
+					textCenterX -= x1*(2.0f+FontScale);
+					textCenterY -= y1*(2.0f+FontScale);
+				}
+
+				// If tick text is angled.. 
+				if (this.TicksLabelAngle != 0.0f)
+				{
+
+					// determine the point we want to rotate text about.
+					
+					PointF textScaledTickVector = new PointF( this.TickScale * x1 * (textSize.Height/2.0f), this.TickScale * y1 * (textSize.Height/2.0f) );
+
+ 					PointF rotatePoint;
+ 					if (this.TickTextNextToAxis) 
+					{
+ 						rotatePoint = new PointF( tickStart.X - textScaledTickVector.X, tickStart.Y - textScaledTickVector.Y );
+ 					}
+ 					else 
+					{
+ 						rotatePoint = new PointF( tickEnd.X + textScaledTickVector.X, tickEnd.Y + textScaledTickVector.Y );
+ 					}
+ 
+ 					float actualAngle;
+					if (flipTicksLabel_) 
+					{
+ 						double radAngle = (Math.PI / 180) * this.TicksLabelAngle;
+ 						rotatePoint.X += textSize.Width * (float)Math.Cos(radAngle);
+ 						rotatePoint.Y += textSize.Width * (float)Math.Sin(radAngle);
+ 						actualAngle = this.TicksLabelAngle + 180;
+ 					}
+ 					else 
+					{
+ 						actualAngle = this.TicksLabelAngle;
+					}
+					
+
+					g.TranslateTransform( rotatePoint.X, rotatePoint.Y );
+
+					g.RotateTransform( actualAngle );
+					
+					Matrix m = g.Transform;
+					PointF[] recPoints = new PointF[2];
+					recPoints[0] = new PointF( 0.0f, -(textSize.Height / 2) );
+					recPoints[1] = new PointF( textSize.Width, textSize.Height );
+					m.TransformPoints( recPoints );
+
+					float t_x1 = Math.Min( recPoints[0].X, recPoints[1].X );
+					float t_x2 = Math.Max( recPoints[0].X, recPoints[1].X );
+					float t_y1 = Math.Min( recPoints[0].Y, recPoints[1].Y );
+					float t_y2 = Math.Max( recPoints[0].Y, recPoints[1].Y );
+					
+					boundingBox = Rectangle.Union(boundingBox, new Rectangle( (int)t_x1, (int)t_y1, (int)(t_x2-t_x1), (int)(t_y2-t_y1) ) );
+					RectangleF drawRect = new RectangleF( 0.0f, -(textSize.Height / 2), textSize.Width, textSize.Height );
+
+					g.DrawString( 
+						text,
+						tickTextFontScaled_,
+						tickTextBrush_, 
+						drawRect,
+						drawFormat_ );
+
+					t_x2 -= tickStart.X;
+					t_y2 -= tickStart.Y;
+					t_x2 *= 1.25f;
+					t_y2 *= 1.25f;
+
+					labelOffset = new Point( (int)t_x2, (int)t_y2 );
+
+					g.ResetTransform();
+
+					//g.DrawRectangle( new Pen(Color.Purple), boundingBox.X, boundingBox.Y, boundingBox.Width, boundingBox.Height );
+
+				}
+				else
+				{
+
+					float bx1 = (textCenterX - textSize.Width/2.0f);
+					float by1 = (textCenterY - textSize.Height/2.0f);
+					float bx2 = textSize.Width;
+					float by2 = textSize.Height;
+
+					RectangleF drawRect = new RectangleF( bx1, by1, bx2, by2 );
+					Rectangle drawRect_int = new Rectangle( (int)bx1, (int)by1, (int)bx2, (int)by2 );
+					// g.DrawRectangle( new Pen(Color.Green), bx1, by1, bx2, by2 );
+
+					boundingBox = Rectangle.Union( boundingBox, drawRect_int );
+
+					// g.DrawRectangle( new Pen(Color.Purple), boundingBox.X, boundingBox.Y, boundingBox.Width, boundingBox.Height );
+
+					g.DrawString( 
+						text,
+						tickTextFontScaled_,
+						tickTextBrush_,
+						drawRect,
+						drawFormat_ );
+
+					textCenterX -= tickStart.X;
+					textCenterY -= tickStart.Y;
+					textCenterX *= 2.3f;
+					textCenterY *= 2.3f;
+
+					labelOffset = new Point( (int)textCenterX, (int)textCenterY );
+				}
+			} 
+
+		}
+
+
+		/// <summary>
+		/// Draw the axis. This involves three steps:
+		///  (1) Draw the axis line.
+		///  (2) Draw the tick marks.
+		///  (3) Draw the label.
+		/// </summary>
+		/// <param name="g">The drawing surface on which to draw.</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="boundingBox">out The bounding rectangle of the axis including axis line, label, tick marks and tick mark labels</param>
+		public virtual void Draw( 
+			System.Drawing.Graphics g,
+			Point physicalMin,
+			Point physicalMax, 
+			out Rectangle boundingBox )
+		{
+			// calculate the bounds of the axis line only.
+			int x1 = Math.Min( physicalMin.X, physicalMax.X );
+			int x2 = Math.Max( physicalMin.X, physicalMax.X );
+			int y1 = Math.Min( physicalMin.Y, physicalMax.Y );
+			int y2 = Math.Max( physicalMin.Y, physicalMax.Y );
+			Rectangle bounds = new Rectangle( x1, y1, x2-x1, y2-y1 );
+
+			if (!Hidden)
+			{
+				
+				// (1) Draw the axis line.
+				g.DrawLine( this.linePen_, physicalMin.X, physicalMin.Y, physicalMax.X, physicalMax.Y );
+
+				// (2) draw tick marks (subclass responsibility). 
+
+				object labelOffset;
+				object tickBounds;
+				this.DrawTicks( g, physicalMin, physicalMax, out labelOffset, out tickBounds );
+
+				// (3) draw the axis label
+				object labelBounds = null;
+				if (!this.HideTickText)
+				{
+					labelBounds = this.DrawLabel( g, (Point)labelOffset, physicalMin, physicalMax );
+				}
+
+				// (4) merge bounds and return.
+				if (labelBounds != null)
+					bounds = Rectangle.Union( bounds, (Rectangle)labelBounds );
+
+				if (tickBounds != null)
+					bounds = Rectangle.Union( bounds, (Rectangle)tickBounds );
+
+			}
+
+			boundingBox = bounds;
+		}
+
+
+		/// <summary>
+		/// Update the bounding box and label offset associated with an axis
+		/// to encompass the additionally specified mergeBoundingBox and 
+		/// mergeLabelOffset respectively.
+		/// </summary>
+		/// <param name="labelOffset">Current axis label offset.</param>
+		/// <param name="boundingBox">Current axis bounding box.</param>
+		/// <param name="mergeLabelOffset">the label offset to merge. The current label offset will be replaced by this if it's norm is larger.</param>
+		/// <param name="mergeBoundingBox">the bounding box to merge. The current bounding box will be replaced by this if null, or by the least upper bound of bother bounding boxes otherwise.</param>
+		protected static void UpdateOffsetAndBounds( 
+			ref object labelOffset, ref object boundingBox, 
+			Point mergeLabelOffset, Rectangle mergeBoundingBox )
+		{
+			// determining largest label offset and use it.
+			Point lo = (Point)labelOffset;
+			double norm1 = Math.Sqrt( lo.X*lo.X + lo.Y*lo.Y );
+			double norm2 = Math.Sqrt( mergeLabelOffset.X*mergeLabelOffset.X + mergeLabelOffset.Y*mergeLabelOffset.Y );
+			if (norm1 < norm2)
+			{
+				labelOffset = mergeLabelOffset;
+			}
+
+			// determining bounding box.
+			Rectangle b = mergeBoundingBox;
+			if (boundingBox == null)
+			{
+				boundingBox = b;
+			}
+			else
+			{
+				boundingBox = Rectangle.Union( (Rectangle)boundingBox, b );
+			}
+		}
+
+
+		/// <summary>
+		/// DrawTicks method. In base axis class this does nothing.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="labelOffset">is set to a suitable offset from the axis to draw the axis label. In this base method, set to null.</param>
+		/// <param name="boundingBox">is set to the smallest box that bounds the ticks and the tick text. In this base method, set to null.</param>
+		protected virtual void DrawTicks( 
+			Graphics g, 
+			Point physicalMin, 
+			Point physicalMax, 
+			out object labelOffset,
+			out object boundingBox )
+		{
+			labelOffset = null;
+			boundingBox = null;
+			// do nothing. This class is not abstract because a subclass may
+			// want to override the Axis.Draw method to one that doesn't 
+			// require DrawTicks.
+		}
+
+
+
+		/// <summary>
+		/// World extent of the axis.
+		/// </summary>
+		public double WorldLength
+		{
+			get
+			{
+				return Math.Abs( worldMax_ - worldMin_ );
+			}
+		}
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the large ticks. 
+		/// When the physical extent of the axis is small, some of the positions 
+		/// that were generated in this pass may be converted to small tick 
+		/// positions and returned as well.
+		/// 
+		/// This default implementation returns empty large ticks list and null
+		/// small tick list.
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">ArrayList containing the positions of the small ticks if calculated, null otherwise.</param>
+		internal virtual void WorldTickPositions_FirstPass(
+			Point physicalMin, 
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+			largeTickPositions = new ArrayList();
+			smallTickPositions = null;
+		}
+
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the small ticks
+		/// if they have not already been generated.
+		/// 
+		/// This default implementation creates an empty smallTickPositions list 
+		/// if it doesn't already exist.
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">The positions of the large ticks.</param>
+		/// <param name="smallTickPositions">If null, small tick positions are returned via this parameter. Otherwise this function does nothing.</param>
+		internal virtual void WorldTickPositions_SecondPass( 
+			Point physicalMin,
+			Point physicalMax,
+			ArrayList largeTickPositions, 
+			ref ArrayList smallTickPositions )
+		{
+			if (smallTickPositions == null)
+				smallTickPositions = new ArrayList();
+		}
+
+
+		/// <summary>
+		/// Determines the positions of all Large and Small ticks.
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">ArrayList containing the positions of the small ticks.</param>
+		public void WorldTickPositions(
+			Point physicalMin,
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+			WorldTickPositions_FirstPass( physicalMin, physicalMax, out largeTickPositions, out smallTickPositions );
+			WorldTickPositions_SecondPass( physicalMin, physicalMax, largeTickPositions, ref smallTickPositions );
+		}
+
+
+		/// <summary>
+		/// Moves the world min and max values so that the world axis
+		/// length is [percent] bigger. If the current world
+		/// max and min values are the same, they are moved appart 
+		/// an arbitrary amount. This arbitrary amount is currently
+		/// 0.01, and will probably be configurable in the future.
+		/// </summary>
+		/// <param name="percent">Percentage to increase world length by.</param>
+		/// <remarks>Works for the case WorldMax is less than WorldMin.</remarks>
+		public void IncreaseRange( double percent )
+		{
+			double range = WorldMax - WorldMin;
+			
+			if ( !Utils.DoubleEqual( range, 0.0 ) )
+			{
+				range *= percent;
+			}
+			else
+			{
+				// arbitrary number. 
+				// TODO make this configurable.
+				range = 0.01;
+			}
+
+			WorldMax += range;
+			WorldMin -= range;
+		}
+
+
+		/// <summary>
+		/// Scale label and tick fonts by this factor. Set by PlotSurface2D 
+		/// Draw method.
+		/// </summary>
+		internal float FontScale 
+		{
+			get 
+			{
+				return fontScale_;
+			}
+ 
+			set 
+			{
+				fontScale_ = value;
+				UpdateScale();
+			}
+		}
+		private float fontScale_;
+
+
+		/// <summary>
+		/// Scale tick mark lengths by this factor. Set by PlotSurface2D
+		/// Draw method.
+		/// </summary>		
+		internal float TickScale 
+		{
+			get 
+			{
+				return tickScale_;
+			}
+			set 
+			{
+				tickScale_ = value;
+			}
+		}
+		private float tickScale_;
+
+
+		private void UpdateScale()	
+		{
+			if (labelFont_ != null)
+				this.labelFontScaled_ = Utils.ScaleFont( labelFont_, FontScale );
+			
+			if (tickTextFont_ != null)
+				this.tickTextFontScaled_ = Utils.ScaleFont( tickTextFont_, FontScale );
+		}
+
+
+		/// <summary>
+		/// Get whether or not this axis is linear.
+		/// </summary>
+		public virtual bool IsLinear
+		{
+			get
+			{
+				return true;
+			}
+		}
+
+		private float labelOffset_ = 0;
+		/// <summary>
+		/// If LabelOffsetAbsolute is false (default) then this is the offset 
+		/// added to default axis label position. If LabelOffsetAbsolute is 
+		/// true, then this is the absolute offset of the label from the axis.
+		/// 
+		/// If positive, offset is further away from axis, if negative, towards
+		/// the axis.
+		/// </summary>
+		public float LabelOffset
+		{
+			get
+			{
+				return labelOffset_;
+			}
+			set
+			{
+				labelOffset_ = value;
+			}
+		}
+
+        private bool labelOffsetAbsolute_ = false;
+        /// <summary>
+        /// If true, the value specified by LabelOffset is the absolute distance
+        /// away from the axis that the label is drawn. If false, the value 
+        /// specified by LabelOffset is added to the pre-calculated value to 
+        /// determine the axis label position.
+        /// </summary>
+        /// <value></value>
+        public bool LabelOffsetAbsolute
+        {
+            get
+            {
+                return labelOffsetAbsolute_;
+            }
+            set
+            {
+                labelOffsetAbsolute_ = value;
+            }
+        }
+
+        private bool labelOffsetScaled_ = true;
+		/// <summary>
+		/// Whether or not the supplied LabelOffset should be scaled by 
+		/// a factor as specified by FontScale.
+		/// </summary>
+		public bool LabelOffsetScaled
+		{
+			get
+			{
+				return labelOffsetScaled_;
+			}
+			set
+			{
+				labelOffsetScaled_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// returns a suitable offset for the axis label in the case that there are no
+		/// ticks or tick text in the way.
+		/// </summary>
+		/// <param name="physicalMin">physical point corresponding to the axis world maximum.</param>
+		/// <param name="physicalMax">physical point corresponding to the axis world minimum.</param>
+		/// <returns>axis label offset</returns>
+		protected Point getDefaultLabelOffset( Point physicalMin, Point physicalMax )
+		{
+			System.Drawing.Rectangle tBoundingBox;
+			System.Drawing.Point tLabelOffset;
+
+			this.DrawTick( null, this.WorldMax, this.LargeTickSize, 
+				"",
+				new Point(0,0),
+				physicalMin, physicalMax,
+				out tLabelOffset, out tBoundingBox );
+
+			return tLabelOffset;
+		}
+
+
+        /// <summary>
+        /// Set the Axis color (sets all of axis line color, Tick text color, and label color).
+        /// </summary>
+        public Color Color
+        {
+            set
+            {
+                this.AxisColor = value;
+                this.TickTextColor = value;
+                this.LabelColor = value;
+            }
+        }
+
+
+        /*
+
+        // finish implementation of this at some point.
+
+        public class WorldValueChangedArgs
+        {
+            public WorldValueChangedArgs( double value, MinMaxType minOrMax )
+            {
+                Value = value;
+                MinOrMax = minOrMax;
+            }
+
+            public double Value;
+
+            public enum MinMaxType
+            {
+                Min = 0,
+                Max = 1
+            }
+
+            public MinMaxType MinOrMax;
+        }
+
+
+        public delegate void WorldValueChangedHandler( object sender, WorldValueChangedArgs e );
+
+        public event WorldValueChangedHandler WorldMinChanged;
+        public event WorldValueChangedHandler WorldMaxChanged;
+        public event WorldValueChangedHandler WorldExtentsChanged;
+
+        */
+
+    }
+}
diff --git a/nplot/nplot/BarPlot.cs b/nplot/nplot/BarPlot.cs
new file mode 100644
index 0000000..69be645
--- /dev/null
+++ b/nplot/nplot/BarPlot.cs
@@ -0,0 +1,298 @@
+/*
+NPlot - A charting library for .NET
+
+BarPlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// Draws 
+	/// </summary>
+	public class BarPlot : BasePlot, IPlot, IDrawable
+	{
+
+		/// <summary>
+		/// Default Constructor
+		/// </summary>
+		public BarPlot()
+		{
+		}
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the ordinate [y] axis.
+		/// </summary>
+		public object OrdinateDataTop
+		{
+			get
+			{
+				return this.ordinateDataTop_;
+			}
+			set
+			{
+				this.ordinateDataTop_ = value;
+			}
+		}
+		private object ordinateDataTop_ = null;
+
+		
+		/// <summary>
+		/// Gets or sets the data, or column name for the ordinate [y] axis.
+		/// </summary>
+		public object OrdinateDataBottom
+		{
+			get
+			{
+				return this.ordinateDataBottom_;
+			}
+			set
+			{
+				this.ordinateDataBottom_ = value;
+			}
+		}
+		private object ordinateDataBottom_ = null;
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the abscissa [x] axis.
+		/// </summary>
+		public object AbscissaData
+		{
+			get
+			{
+				return this.abscissaData_;
+			}
+			set
+			{
+				this.abscissaData_ = value;
+			}
+		}
+		private object abscissaData_ = null;
+
+
+		/// <summary>
+		/// Draws the line plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			SequenceAdapter dataTop = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataTop, this.AbscissaData );
+
+			SequenceAdapter dataBottom = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataBottom, this.AbscissaData );
+
+			ITransform2D t = Transform2D.GetTransformer( xAxis, yAxis );
+			
+			for (int i=0; i<dataTop.Count; ++i)
+			{
+				PointF physicalBottom = t.Transform( dataBottom[i] );
+				PointF physicalTop = t.Transform( dataTop[i] );
+
+				if (physicalBottom != physicalTop) 
+				{
+					Rectangle r = new Rectangle( (int)(physicalBottom.X - BarWidth/2), (int)physicalTop.Y,
+						(int)BarWidth, (int)(physicalBottom.Y - physicalTop.Y) );
+
+					g.FillRectangle( this.rectangleBrush_.Get(r), r );
+					g.DrawRectangle( borderPen_, r );
+				}
+			}
+
+		}
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			SequenceAdapter dataBottom_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataBottom, this.AbscissaData );
+
+			SequenceAdapter dataTop_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataTop, this.AbscissaData );
+
+			Axis axis = dataTop_.SuggestXAxis();
+			axis.LUB(dataBottom_.SuggestXAxis());
+			return axis;
+		}
+
+
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+			SequenceAdapter dataBottom_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataBottom, this.AbscissaData );
+
+			SequenceAdapter dataTop_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateDataTop, this.AbscissaData );
+
+			Axis axis = dataTop_.SuggestYAxis();
+			axis.LUB(dataBottom_.SuggestYAxis());
+			return axis;
+		}
+
+
+		/// <summary>
+		/// Draws a representation of this plot in the legend.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public virtual void DrawInLegend(Graphics g, Rectangle startEnd)
+		{
+			int smallerHeight = (int)(startEnd.Height * 0.5f);
+			int heightToRemove = (int)(startEnd.Height * 0.5f);
+			Rectangle newRectangle = new Rectangle( startEnd.Left, startEnd.Top + smallerHeight / 2, startEnd.Width, smallerHeight );
+			g.FillRectangle( rectangleBrush_.Get( newRectangle ), newRectangle );
+			g.DrawRectangle( borderPen_, newRectangle );
+		}
+
+		/// <summary>
+		/// The pen used to draw the plot
+		/// </summary>
+		public System.Drawing.Pen BorderPen
+		{
+			get
+			{
+				return borderPen_;
+			}
+			set
+			{
+				borderPen_ = value;
+			}
+		}
+		private System.Drawing.Pen borderPen_ = new Pen(Color.Black);
+
+
+		/// <summary>
+		/// The color of the pen used to draw lines in this plot.
+		/// </summary>
+		public System.Drawing.Color BorderColor
+		{
+			set
+			{
+				if (borderPen_ != null)
+				{
+					borderPen_.Color = value;
+				}
+				else
+				{
+					borderPen_ = new Pen(value);
+				}
+			}
+			get
+			{
+				return borderPen_.Color;
+			}
+		}
+
+
+		/// <summary>
+		/// Set/Get the fill brush
+		/// </summary>
+		public IRectangleBrush FillBrush
+		{
+			get
+			{
+				return rectangleBrush_;
+			}
+			set
+			{
+				rectangleBrush_ = value;
+			}
+
+		}
+		private IRectangleBrush rectangleBrush_ = new RectangleBrushes.Solid( Color.LightGray );
+
+
+		/// <summary>
+		/// Set/Get the width of the bar in physical pixels.
+		/// </summary>
+		public float BarWidth			         
+		{
+			get 
+			{ 
+				return barWidth_; 
+			}
+			set 
+			{ 
+				barWidth_ = value; 
+			}
+		}
+        private float barWidth_ = 8;
+
+
+		/// <summary>
+		/// Write data associated with the plot as text.
+		/// </summary>
+		/// <param name="sb">the string builder to write to.</param>
+		/// <param name="region">Only write out data in this region if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, only data in region is written, else all data is written.</param>
+		/// <remarks>TODO: not implemented.</remarks>
+		public void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion )
+		{
+			sb.Append( "Write data not implemented yet for BarPlot\r\n" );
+		}
+
+
+	}
+
+}
diff --git a/nplot/nplot/BasePlot.cs b/nplot/nplot/BasePlot.cs
new file mode 100644
index 0000000..0fc3c8b
--- /dev/null
+++ b/nplot/nplot/BasePlot.cs
@@ -0,0 +1,138 @@
+/*
+NPlot - A charting library for .NET
+
+BasePlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// Supplies implementation of basic legend handling properties, and
+	/// basic data specifying properties which are used by all plots.
+	/// </summary>
+	/// <remarks>If C# had multiple inheritance, the heirachy would be different.</remarks>
+	public abstract class BasePlot 
+	{
+
+		/// <summary>
+		/// A label to associate with the plot - used in the legend.
+		/// </summary>
+		public string Label
+		{
+			get
+			{
+				return label_;
+			}
+			set
+			{
+				this.label_ = value;
+			}
+		}
+		
+		private string label_ = "";
+
+
+		/// <summary>
+		/// Whether or not to include an entry for this plot in the legend if it exists.
+		/// </summary>
+		public bool ShowInLegend
+		{
+			get
+			{
+				return showInLegend_;
+			}
+			set
+			{
+				this.showInLegend_ = value;
+			}
+		}
+		private bool showInLegend_ = true;
+
+
+		/// <summary>
+		/// Gets or sets the source containing a list of values used to populate the plot object.
+		/// </summary>
+		public object DataSource
+		{
+			get
+			{
+				return this.dataSource_;
+			}
+			set
+			{
+				this.dataSource_ = value;
+			}
+		}
+		private object dataSource_ = null;
+
+
+		/// <summary>
+		/// Gets or sets the specific data member in a multimember data source to get data from.
+		/// </summary>
+		public string DataMember
+		{
+			get
+			{
+				return this.dataMember_;
+			}
+			set
+			{
+				this.dataMember_ = value;
+			}
+		}
+		private string dataMember_ = null;
+
+
+
+	}
+}
diff --git a/nplot/nplot/BasePlot3D.cs b/nplot/nplot/BasePlot3D.cs
new file mode 100644
index 0000000..c9b86b2
--- /dev/null
+++ b/nplot/nplot/BasePlot3D.cs
@@ -0,0 +1,96 @@
+// ******** experimental ******** 
+/*
+using System;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Functionality shared amoungst all 3D plots. TODO: Not implemented. 
+	/// </summary>
+	public class BasePlot3D
+	{
+
+		/// <summary>
+		/// Constructor.
+		/// </summary>
+		public BasePlot3D()
+		{
+		}
+
+		// DATA SPECIFIERS
+
+		// in first instance I think just have a DataSource, with
+		// the only way of specifying points as an ArrayList of 
+		// point3s
+
+		/// <summary>
+		/// Gets or sets the source containing a list of values used to populate the plot object.
+		/// </summary>
+		public object DataSource
+		{
+			get
+			{
+				return dataSource_;
+			}
+			set
+			{
+				dataSource_ = value;
+			}
+		}
+		object dataSource_ = null;
+
+
+		need a SequenceAdapter3D to interpret all this crap - 
+		probably build on SequenceAdapter.
+		
+		public string DataMember
+		{
+			get
+			{
+				return dataMember_;
+			}
+			set
+			{
+				dataMember_ = value;
+			}
+		}
+		string dataMember_ = null;
+
+
+		// these are needed for access to table columns.
+		
+		public object XData
+		{
+			get
+			{
+			}
+			set
+			{
+			}
+		}
+
+		public object YData
+		{
+			get
+			{
+			}
+			set
+			{
+			}
+		}
+
+		public object ZData
+		{
+			get
+			{
+			}
+			set
+			{
+			}
+		}
+
+
+	}
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/BaseSequenceLinePlot.cs b/nplot/nplot/BaseSequenceLinePlot.cs
new file mode 100644
index 0000000..d5301c9
--- /dev/null
+++ b/nplot/nplot/BaseSequenceLinePlot.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Drawing;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// supplies implementation of basic functionality for plots based on drawing
+	/// lines [line, step and histogram].
+	/// </summary>
+	/// <remarks>If C# had multiple inheritance, the heirachy would be different. The way it is isn't very nice.</remarks>
+	public class BaseSequenceLinePlot : BaseSequencePlot, ISequencePlot
+	{
+
+		/// <summary>
+		/// Draws a representation of this plot in the legend.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public virtual void DrawInLegend( Graphics g, Rectangle startEnd )
+		{
+			g.DrawLine( pen_, startEnd.Left, (startEnd.Top + startEnd.Bottom)/2, 
+				startEnd.Right, (startEnd.Top + startEnd.Bottom)/2 );
+		}
+
+
+		/// <summary>
+		/// The pen used to draw the plot
+		/// </summary>
+		public System.Drawing.Pen Pen
+		{
+			get
+			{
+				return pen_;
+			}
+			set
+			{
+				pen_ = value;
+			}
+		}
+		private System.Drawing.Pen pen_ = new Pen( Color.Black );
+
+
+		/// <summary>
+		/// The color of the pen used to draw lines in this plot.
+		/// </summary>
+		public System.Drawing.Color Color
+		{
+			set
+			{
+				if (pen_ != null)
+				{
+					pen_.Color = value;
+				}
+				else
+				{
+					pen_ = new Pen( value );
+				}
+			}
+			get
+			{
+				return pen_.Color;
+			}
+		}
+
+
+	}
+
+}
diff --git a/nplot/nplot/BaseSequencePlot.cs b/nplot/nplot/BaseSequencePlot.cs
new file mode 100644
index 0000000..eeef585
--- /dev/null
+++ b/nplot/nplot/BaseSequencePlot.cs
@@ -0,0 +1,118 @@
+/*
+NPlot - A charting library for .NET
+
+BaseSequencePlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Adds additional basic functionality to BasePlot that is common to all
+	/// plots that implement the ISequencePlot interface.
+	/// </summary>
+	/// <remarks>If C# had multiple inheritance, the heirachy would be different. The way it is isn't very nice.</remarks>
+	public class BaseSequencePlot : BasePlot, ISequencePlot
+	{
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the ordinate [y] axis.
+		/// </summary>
+		public object OrdinateData
+		{
+			get
+			{
+				return this.ordinateData_;
+			}
+			set
+			{
+				this.ordinateData_ = value;
+			}
+		}
+		private object ordinateData_ = null;
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the abscissa [x] axis.
+		/// </summary>
+		public object AbscissaData
+		{
+			get
+			{
+				return this.abscissaData_;
+			}
+			set
+			{
+				this.abscissaData_ = value;
+			}
+		}
+		private object abscissaData_ = null;
+
+
+		/// <summary>
+		/// Writes text data of the plot object to the supplied string builder. It is 
+		/// possible to specify that only data in the specified range be written.
+		/// </summary>
+		/// <param name="sb">the StringBuilder object to write to.</param>
+		/// <param name="region">a region used if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, only data enclosed in the provided region will be written.</param>
+		public void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion )
+		{
+			SequenceAdapter data_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			sb.Append( "Label: " );
+			sb.Append( this.Label );
+			sb.Append( "\r\n" );
+			data_.WriteData( sb, region, onlyInRegion );
+		}
+
+	}
+}
diff --git a/nplot/nplot/Bitmap.PlotSurface2D.cs b/nplot/nplot/Bitmap.PlotSurface2D.cs
new file mode 100644
index 0000000..9a27b6c
--- /dev/null
+++ b/nplot/nplot/Bitmap.PlotSurface2D.cs
@@ -0,0 +1,541 @@
+/*
+NPlot - A charting library for .NET
+
+Bitmap.PlotSurface2D.cs
+Copyright (C) 2003 
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+using System.Collections;
+
+namespace NPlot
+{
+
+	namespace Bitmap
+	{
+
+		/// <summary>
+		/// Wrapper around NPlot.PlotSurface2D that provides extra functionality
+		/// specific to drawing to Bitmaps.
+		/// </summary>
+		public class PlotSurface2D: IPlotSurface2D 
+		{
+
+			/// <summary>
+			/// Constructor.
+			/// </summary>
+			/// <param name="width">width of the bitmap.</param>
+			/// <param name="height">height of the bitmap.</param>
+			public PlotSurface2D( int width, int height )
+			{
+				b_ = new System.Drawing.Bitmap( width, height );
+				ps_ = new NPlot.PlotSurface2D();	
+			}
+
+			/// <summary>
+			/// Constructor.
+			/// </summary>
+			/// <param name="b">The Bitmap where the plot is to be rendered.</param>
+			public PlotSurface2D( System.Drawing.Bitmap b )
+			{
+				b_ = b;
+				ps_ = new NPlot.PlotSurface2D();	
+			}
+
+
+			/// <summary>
+			/// Renders the plot.
+			/// </summary>
+			/// <param name="g">The graphics surface.</param>
+			/// <param name="bounds">The rectangle storing the bounds for rendering.</param>
+			public void Draw( Graphics g, Rectangle bounds )
+			{
+				ps_.Draw( g, bounds );
+			}
+
+
+			/// <summary>
+			/// Clears the plot.
+			/// </summary>
+			public void Clear()
+			{
+				ps_.Clear();
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+			/// the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">The IDrawable object to add to the plot surface.</param>
+			public void Add( IDrawable p )
+			{
+				ps_.Add( p );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface against the specified axes. If
+			/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">the IDrawable object to add to the plot surface</param>
+			/// <param name="xp">the x-axis to add the plot against.</param>
+			/// <param name="yp">the y-axis to add the plot against.</param>
+			public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp, NPlot.PlotSurface2D.YAxisPosition yp )
+			{
+				ps_.Add( p, xp, yp );
+			}
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+			/// the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">The IDrawable object to add to the plot surface.</param>
+			/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+			public void Add( IDrawable p, int zOrder )
+			{
+				ps_.Add( p, zOrder );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface against the specified axes. If
+			/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">the IDrawable object to add to the plot surface</param>
+			/// <param name="xp">the x-axis to add the plot against.</param>
+			/// <param name="yp">the y-axis to add the plot against.</param>
+			/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+			public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp,
+				NPlot.PlotSurface2D.YAxisPosition yp, int zOrder )
+			{
+				ps_.Add( p, xp, yp , zOrder);
+			}
+
+			/// <summary>
+			/// The plot surface title.
+			/// </summary>
+			public string Title
+			{
+				get 
+				{
+					return ps_.Title;
+				}
+				set 
+				{
+					ps_.Title = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The plot title font.
+			/// </summary>
+			public Font TitleFont 
+			{
+				get 
+				{
+					return ps_.TitleFont;
+				}
+				set 
+				{
+					ps_.TitleFont = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The distance in pixels to leave between of the edge of the bounding rectangle
+			/// supplied to the Draw method, and the markings that make up the plot.
+			/// </summary>
+			public int Padding
+			{
+				get
+				{
+					return ps_.Padding;
+				}
+				set
+				{
+					ps_.Padding = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The bottom abscissa axis.
+			/// </summary>
+			public Axis XAxis1
+			{
+				get
+				{
+					return ps_.XAxis1;
+				}
+				set
+				{
+					ps_.XAxis1 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The left ordinate axis.
+			/// </summary>
+			public Axis YAxis1
+			{
+				get
+				{
+					return ps_.YAxis1;
+				}
+				set
+				{
+					ps_.YAxis1 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The top abscissa axis.
+			/// </summary>
+			public Axis XAxis2
+			{
+				get
+				{
+					return ps_.XAxis2;
+				}
+				set
+				{
+					ps_.XAxis2 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The right ordinate axis.
+			/// </summary>
+			public Axis YAxis2
+			{
+				get
+				{
+					return ps_.YAxis2;
+				}
+				set
+				{
+					ps_.YAxis2 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Gets or Sets the legend to use with this plot surface.
+			/// </summary>
+			public NPlot.Legend Legend
+			{
+				get
+				{
+					return ps_.Legend;
+				}
+				set
+				{
+					ps_.Legend = value;
+				}
+			}
+
+			/// <summary>
+			/// Gets or Sets the legend z-order.
+			/// </summary>
+			public int LegendZOrder
+			{
+				get
+				{
+					return ps_.LegendZOrder;
+				}
+				set
+				{
+					ps_.LegendZOrder = value;
+				}
+			}
+
+			/// <summary>
+			/// A color used to paint the plot background. Mutually exclusive with PlotBackImage and PlotBackBrush
+			/// </summary>
+			public System.Drawing.Color PlotBackColor
+			{
+				set
+				{
+					ps_.PlotBackColor = value;
+				}
+			}
+
+
+			/// <summary>
+			/// An imaged used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+			/// </summary>
+			public System.Drawing.Bitmap PlotBackImage
+			{
+				set
+				{
+					ps_.PlotBackImage = value;
+				}
+			}
+
+
+			/// <summary>
+			/// A Rectangle brush used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+			/// </summary>
+			public IRectangleBrush PlotBackBrush
+			{
+				set
+				{
+					ps_.PlotBackBrush = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Smoothing mode to use when drawing plots.
+			/// </summary>
+			public System.Drawing.Drawing2D.SmoothingMode SmoothingMode 
+			{ 
+				get
+				{
+					return ps_.SmoothingMode;
+				}
+				set
+				{
+					ps_.SmoothingMode = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The bitmap width
+			/// </summary>
+			public int Width 
+			{
+				get
+				{
+					return b_.Width;
+				}
+			}
+
+
+			/// <summary>
+			/// The bitmap height
+			/// </summary>
+			public int Height 
+			{
+				get 
+				{
+					return b_.Height;
+				}
+			}
+
+
+			/// <summary>
+			/// Renders the bitmap to a MemoryStream. Useful for returning the bitmap from
+			/// an ASP.NET page.
+			/// </summary>
+			/// <returns>The MemoryStream object.</returns>
+			public System.IO.MemoryStream ToStream( System.Drawing.Imaging.ImageFormat imageFormat ) 
+			{
+				System.IO.MemoryStream stream = new System.IO.MemoryStream();
+				ps_.Draw(Graphics.FromImage(this.Bitmap),new System.Drawing.Rectangle(0,0,b_.Width,b_.Height));
+				this.Bitmap.Save(stream, imageFormat);
+				return stream;
+			}
+
+
+			/// <summary>
+			/// The bitmap to use as the drawing surface.
+			/// </summary>
+			public System.Drawing.Bitmap Bitmap 
+			{
+				get 
+				{
+					return b_;
+				}
+				set
+				{
+					b_ = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The bitmap background color outside the bounds of the plot surface.
+			/// </summary>
+			public Color BackColor
+			{
+				set
+				{
+					backColor_ = value;
+				}
+			}
+			object backColor_ = null;
+			
+
+			/// <summary>
+			/// Refreshes (draws) the plot.
+			/// </summary>
+			public void Refresh()
+			{
+				if (this.backColor_!=null)
+				{
+					Graphics g = Graphics.FromImage( b_ );
+					g.FillRectangle( (new Pen( (Color)this.backColor_)).Brush,0,0,b_.Width,b_.Height );
+				}
+				ps_.Draw( Graphics.FromImage(b_), new System.Drawing.Rectangle(0,0,b_.Width,b_.Height) );
+			}
+
+
+			private NPlot.PlotSurface2D ps_;
+			private System.Drawing.Bitmap b_;
+
+			
+			/// <summary>
+			/// Add an axis constraint to the plot surface. Axis constraints can
+			/// specify relative world-pixel scalings, absolute axis positions etc.
+			/// </summary>
+			/// <param name="c">The axis constraint to add.</param>
+			public void AddAxesConstraint( AxesConstraint c )
+			{
+				ps_.AddAxesConstraint( c );
+			}
+
+
+			/// <summary>
+			/// Whether or not the title will be scaled according to size of the plot 
+			/// surface.
+			/// </summary>
+			public bool AutoScaleTitle
+			{
+				get
+				{
+					return ps_.AutoScaleTitle;
+				}
+				set
+				{
+					ps_.AutoScaleTitle = value;
+				}
+			}
+
+
+			/// <summary>
+			/// When plots are added to the plot surface, the axes they are attached to
+			/// are immediately modified to reflect data of the plot. If 
+			/// AutoScaleAutoGeneratedAxes is true when a plot is added, the axes will
+			/// be turned in to auto scaling ones if they are not already [tick marks,
+			/// tick text and label size scaled to size of plot surface]. If false,
+			/// axes will not be autoscaling.
+			/// </summary>
+			public bool AutoScaleAutoGeneratedAxes
+			{
+				get
+				{
+					return ps_.AutoScaleAutoGeneratedAxes;
+				}
+				set
+				{
+					ps_.AutoScaleAutoGeneratedAxes = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Sets the title to be drawn using a solid brush of this color.
+			/// </summary>
+			public Color TitleColor
+			{
+				set
+				{
+					ps_.TitleColor = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The brush used for drawing the title.
+			/// </summary>
+			public Brush TitleBrush
+			{
+				get
+				{
+					return ps_.TitleBrush;
+				}
+				set
+				{
+					ps_.TitleBrush = value;
+				}
+			}
+
+            /// <summary>
+            /// Remove a drawable object from the plot surface.
+            /// </summary>
+            /// <param name="p">the drawable to remove</param>
+            /// <param name="updateAxes">whether or not to update the axes after removing the idrawable.</param>
+            public void Remove(IDrawable p, bool updateAxes)
+            {
+                ps_.Remove(p, updateAxes);
+            }
+
+
+			/// <summary>
+			/// Gets an array list containing all drawables currently added to the PlotSurface2D.
+			/// </summary>
+			public ArrayList Drawables
+			{
+				get
+				{
+					return ps_.Drawables;
+				}
+			}
+
+		}
+	}
+}
diff --git a/nplot/nplot/BubblePlot.cs b/nplot/nplot/BubblePlot.cs
new file mode 100644
index 0000000..6cb95f8
--- /dev/null
+++ b/nplot/nplot/BubblePlot.cs
@@ -0,0 +1,72 @@
+using System;
+
+/*
+namespace NPlot
+{
+	/// <summary>
+	/// Summary description for BubblePlot.
+	/// </summary>
+	public class BubblePlot : BasePlot, IPlot
+	{
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public BubblePlot()
+		{
+
+		}
+
+
+		public void DrawInLegend(System.Drawing.Graphics g, System.Drawing.Rectangle startEnd)
+		{
+			// TODO:  Add BubblePlot.DrawInLegend implementation
+		}
+
+		public string Label
+		{
+			get
+			{
+				// TODO:  Add BubblePlot.Label getter implementation
+				return null;
+			}
+			set
+			{
+				// TODO:  Add BubblePlot.Label setter implementation
+			}
+		}
+
+		public bool ShowInLegend
+		{
+			get
+			{
+				// TODO:  Add BubblePlot.ShowInLegend getter implementation
+				return false;
+			}
+			set
+			{
+				// TODO:  Add BubblePlot.ShowInLegend setter implementation
+			}
+		}
+
+		public Axis SuggestXAxis()
+		{
+			// TODO:  Add BubblePlot.SuggestXAxis implementation
+			return null;
+		}
+
+		public Axis SuggestYAxis()
+		{
+			// TODO:  Add BubblePlot.SuggestYAxis implementation
+			return null;
+		}
+
+
+		public void Draw(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis)
+		{
+			// TODO:  Add BubblePlot.Draw implementation
+		}
+
+	}
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/CandlePlot.cs b/nplot/nplot/CandlePlot.cs
new file mode 100644
index 0000000..7ce018c
--- /dev/null
+++ b/nplot/nplot/CandlePlot.cs
@@ -0,0 +1,891 @@
+/*
+NPlot - A charting library for .NET
+
+CandlePlot.cs
+Copyright (C) 2003
+Matt Howlett
+Pawel Konieczny 
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Data;
+
+namespace NPlot
+{
+
+    /// <summary>
+	/// Encapsulates open, low, high and close values useful for specifying financial data
+	/// over a time period, together with a [single] x-value indicating the time [period] the
+	/// data corresponds to. 
+	/// </summary>
+	public class PointOLHC
+	{
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="x">value representing the time period that the financial values refer to</param>
+		/// <param name="open">The value at open of time period.</param>
+		/// <param name="low">The low value over the time period</param>
+		/// <param name="high">The high value over the time period.</param>
+		/// <param name="close">The value at close of time period.</param>
+		public PointOLHC( double x, double open, double low, double high, double close )
+		{
+			this.x_ = x;
+			this.open_ = open;
+			this.close_ = close;
+			this.low_ = low;
+			this.high_ = high;
+		}
+
+
+		/// <summary>
+		/// value representing the time period that the financial values apply to.
+		/// </summary>
+		public double X
+		{
+			get
+			{
+				return x_;
+			}
+			set
+			{
+				this.x_ = value;
+			}
+		}
+		private double x_;
+
+
+		/// <summary>
+		/// The value at open of time period.
+		/// </summary>
+		public double Open
+		{
+			get
+			{
+				return open_;
+			}
+			set
+			{
+				open_ = value;
+			}
+		}
+		private double open_;
+
+
+		/// <summary>
+		/// The value at close of time period.
+		/// </summary>
+		public double Close
+		{
+			get
+			{
+				return close_;
+			}
+			set
+			{
+				close_ = value;
+			}
+		}
+		private double close_;
+
+
+		/// <summary>
+		/// Low value of the time period.
+		/// </summary>
+		public double Low
+		{
+			get
+			{
+				return low_;
+			}
+			set
+			{
+				low_ = value;
+			}
+		}
+		private double low_;
+
+
+		/// <summary>
+		/// High value of the time period.
+		/// </summary>
+		public double High
+		{
+			get
+			{
+				return high_;
+			}
+			set
+			{
+				high_ = value;
+			}
+		}
+		private double high_;
+
+	}
+
+
+	/// <summary>
+	/// Encapsulates functionality for drawing finacial candle charts. 
+	/// </summary>
+	public class CandlePlot : BasePlot, IPlot
+	{
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public abstract class CandleStyle
+        {
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="d"></param>
+            /// <returns></returns>
+            public abstract CandleStyle Create(CandleDataAdapter d);
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public class Stick : CandleStyle
+        {
+            private Stick() { }
+
+            /// <summary>
+            /// 
+            /// </summary>
+            /// <param name="d"></param>
+            /// <returns></returns>
+            public override CandleStyle Create(CandleDataAdapter d)
+            {
+                return new Stick();
+            }
+        }
+        
+
+        /// <summary>
+        /// This class is responsible for interpreting the various ways you can 
+	    /// specify data to CandlePlot objects
+        /// </summary>
+        public class CandleDataAdapter
+		{
+			private object openData_;
+			private object lowData_;
+			private object highData_;
+			private object closeData_;
+			private object abscissaData_;
+
+			private object dataSource_;
+			private string dataMember_;
+			DataRowCollection rows_ = null;
+
+			// speed optimizations if data is double.
+			private double[] openDataArray_;
+			private double[] lowDataArray_;
+			private double[] highDataArray_;
+			private double[] closeDataArray_;
+			private double[] abscissaDataArray_;
+			private bool useDoublesArrays_;
+            
+
+			/// <summary>
+            /// Constructor
+            /// </summary>
+            /// <param name="dataSource"></param>
+            /// <param name="dataMember"></param>
+            /// <param name="abscissaData"></param>
+            /// <param name="openData"></param>
+            /// <param name="lowData"></param>
+            /// <param name="highData"></param>
+            /// <param name="closeData"></param>
+			public CandleDataAdapter( 
+				object dataSource, string dataMember, object abscissaData,
+				object openData, object lowData, object highData, object closeData )
+			{
+				this.openData_ = openData;
+				this.lowData_ = lowData;
+				this.highData_ = highData;
+				this.closeData_ = closeData;
+				this.abscissaData_ = abscissaData;
+
+				this.dataSource_ = dataSource;
+				this.dataMember_ = dataMember;
+
+				if (dataSource_ != null)
+				{
+					if ( dataSource_ is DataSet )
+					{
+						if (dataMember_ != null)
+						{
+							rows_ = ((DataTable)((DataSet)dataSource_).Tables[dataMember_]).Rows;
+						}
+						else
+						{
+							rows_ = ((DataTable)((DataSet)dataSource_).Tables[0]).Rows;
+						}
+					}
+
+					else if (dataSource_ is DataTable )
+					{
+						rows_ = ((DataTable)dataSource_).Rows;
+					}
+
+					else
+					{
+						throw new NPlotException ( "not implemented yet" );
+					}
+				}
+
+				openDataArray_ = openData_ as System.Double[];
+				lowDataArray_ = lowData_ as System.Double[];
+				highDataArray_ = highData_ as System.Double[];
+				closeDataArray_ = closeData_ as System.Double[];
+				abscissaDataArray_ = abscissaData_ as System.Double[];
+				useDoublesArrays_ = (
+					openDataArray_ != null &&
+					lowDataArray_ != null &&
+					highDataArray_ != null &&
+					lowDataArray_ != null &&
+					abscissaDataArray_ != null
+				);
+
+			}
+
+
+            /// <summary>
+            /// Gets the ith point in the candle adapter
+            /// </summary>
+            /// <param name="i">index of datapoint to get</param>
+            /// <returns>the datapoint.</returns>
+			public PointOLHC this[int i]
+			{
+				get
+				{
+                    
+					// try a fast track first
+                    if (useDoublesArrays_)
+                    {
+                        return new PointOLHC(
+                            abscissaDataArray_[i],
+                            openDataArray_[i],
+                            lowDataArray_[i],
+                            highDataArray_[i],
+                            closeDataArray_[i]);
+                    }
+
+                    // is the data coming from a data source?
+                    else if (rows_ != null)
+                    {
+                        double x = Utils.ToDouble(((DataRow)(rows_[i]))[(string)abscissaData_]);
+                        double open = Utils.ToDouble(((DataRow)(rows_[i]))[(string)openData_]);
+                        double low = Utils.ToDouble(((DataRow)(rows_[i]))[(string)lowData_]);
+                        double high = Utils.ToDouble(((DataRow)(rows_[i]))[(string)highData_]);
+                        double close = Utils.ToDouble(((DataRow)(rows_[i]))[(string)closeData_]);
+
+                        return new PointOLHC(x, open, low, high, close);
+                    }
+
+					// the data is coming from individual arrays.
+                    else if (abscissaData_ is Array && openData_ is Array && lowData_ is Array && highData_ is Array && closeData_ is Array)
+                    {
+                        double x = Utils.ToDouble(((Array)abscissaData_).GetValue(i));
+                        double open = Utils.ToDouble(((Array)openData_).GetValue(i));
+                        double low = Utils.ToDouble(((Array)lowData_).GetValue(i));
+                        double high = Utils.ToDouble(((Array)highData_).GetValue(i));
+                        double close = Utils.ToDouble(((Array)closeData_).GetValue(i));
+
+                        return new PointOLHC(x, open, low, high, close);
+                    }
+
+                    else
+                    {
+                        throw new NPlotException("not implemented yet");
+                    }
+
+                }
+			}
+
+
+            /// <summary>
+            /// The number of datapoints available via the candle adapter.
+            /// </summary>
+            /// <value>the number of datapoints available.</value>
+			public int Count
+			{
+				get
+				{
+					// this is inefficient [could set up delegates in constructor].
+
+					if (useDoublesArrays_)
+					{
+						return openDataArray_.Length;
+					}
+
+					if (openData_ == null)
+					{
+						return 0;
+					}
+
+					if (rows_ != null)
+					{
+						return rows_.Count;
+					}
+
+                    if (openData_ is Array)
+                    {
+                        int size = ((Array)openData_).Length;
+                        if (size != ((Array)closeData_).Length)
+                            throw new NPlotException("open and close arrays are not of same length");
+                        if (size != ((Array)lowData_).Length)
+                            throw new NPlotException("open and close arrays are not of same length");
+                        if (size != ((Array)highData_).Length)
+                            throw new NPlotException("open and close arrays are not of same length");
+                        return size;
+                    }
+
+                    throw new NPlotException( "data not in correct format" );
+				}
+			}
+
+
+			/// <summary>
+			/// Returns an x-axis that is suitable for drawing the data.
+			/// </summary>
+			/// <returns>A suitable x-axis.</returns>
+			public Axis SuggestXAxis()
+			{
+				double min;
+				double max;
+				double minStep = 0.0;
+
+                if (this.rows_ == null)
+                {
+                    Utils.ArrayMinMax((System.Collections.IList)this.abscissaData_, out min, out max);
+
+                    if (((System.Collections.IList)abscissaData_).Count > 1)
+                    {
+                        double first = Utils.ToDouble(((Array)abscissaData_).GetValue(0));
+                        double second = Utils.ToDouble(((Array)abscissaData_).GetValue(1));
+                        minStep = Math.Abs(second - first);
+                    }
+                    if (((System.Collections.IList)abscissaData_).Count > 2)
+                    {
+                        double first = Utils.ToDouble(((Array)abscissaData_).GetValue(1));
+                        double second = Utils.ToDouble(((Array)abscissaData_).GetValue(2));
+                        if (Math.Abs(second - first) < minStep)
+                            minStep = Math.Abs(second - first);
+                    }
+                    if (((System.Collections.IList)abscissaData_)[0] is DateTime)
+                    {
+                        return new DateTimeAxis(min - minStep / 2.0, max + minStep / 2.0);
+                    }
+                    else
+                    {
+                        return new LinearAxis(min - minStep / 2.0, max + minStep / 2.0);
+                    }
+                }
+                else
+                {
+
+                    Utils.RowArrayMinMax(this.rows_, out min, out max, (string)this.abscissaData_);
+
+                    if (rows_.Count > 1)
+                    {
+                        double first = Utils.ToDouble(rows_[0][(string)abscissaData_]);
+                        double second = Utils.ToDouble(rows_[1][(string)abscissaData_]);
+                        minStep = Math.Abs(second - first);
+                    }
+                    if (rows_.Count > 2)
+                    {
+                        double first = Utils.ToDouble(rows_[1][(string)abscissaData_]);
+                        double second = Utils.ToDouble(rows_[2][(string)abscissaData_]);
+                        if (Math.Abs(second - first) < minStep)
+                            minStep = Math.Abs(second - first);
+                    }
+
+                    if ((rows_[0])[(string)abscissaData_] is DateTime)
+                    {
+                        return new DateTimeAxis(min - minStep / 2.0, max + minStep / 2.0);
+                    }
+                    else
+                    {
+                        return new LinearAxis(min - minStep / 2.0, max + minStep / 2.0);
+                    }
+                }
+
+            }
+
+
+			/// <summary>
+			/// Returns a y-axis that is suitable for drawing the data.
+			/// </summary>
+			/// <returns>A suitable y-axis.</returns>
+			public Axis SuggestYAxis()
+			{
+
+				double min_l;
+				double max_l;
+				double min_h;
+				double max_h;
+
+                if (this.rows_ == null)
+                {
+                    Utils.ArrayMinMax((System.Collections.IList)lowData_, out min_l, out max_l);
+                    Utils.ArrayMinMax((System.Collections.IList)highData_, out min_h, out max_h);
+                }
+                else
+                {
+                    Utils.RowArrayMinMax(this.rows_, out min_l, out max_l, (string)this.lowData_);
+                    Utils.RowArrayMinMax(this.rows_, out min_h, out max_h, (string)this.highData_);
+                }
+
+                Axis a = new LinearAxis( min_l, max_h );
+				a.IncreaseRange( 0.08 );
+				return a;
+			}
+
+		}
+
+		
+		/// <summary>
+		/// Default constructor.
+		/// </summary>
+		public CandlePlot()
+		{
+		}
+
+
+        /// <summary>
+        /// Calculates the physical (not world) separation between abscissa values.
+        /// </summary>
+        /// <param name="cd">Candle adapter containing data</param>
+        /// <param name="xAxis">Physical x axis the data is plotted against.</param>
+        /// <returns>physical separation between abscissa values.</returns>
+		private static float CalculatePhysicalSeparation( CandleDataAdapter cd, PhysicalAxis xAxis )
+		{
+			if (cd.Count > 1)
+			{
+				int xPos1 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[0]).X, false )).X;
+				int xPos2 = (int)(xAxis.WorldToPhysical( ((PointOLHC)cd[1]).X, false )).X;		
+				int minDist = xPos2 - xPos1;
+
+				if (cd.Count > 2)
+				{  // to be pretty sure we get the smallest gap.
+					int xPos3 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[2]).X, false)).X;
+					if (xPos3 - xPos2 < minDist) minDist = xPos3 - xPos2;
+
+					if (cd.Count > 3)
+					{
+						int xPos4 = (int)(xAxis.WorldToPhysical(((PointOLHC)cd[3]).X, false)).X;
+						if (xPos4 - xPos3 < minDist) minDist = xPos4 - xPos3;
+					}
+				}
+
+				return minDist;
+			}
+
+            return 0.0f;
+        }
+
+
+		/// <summary>
+		/// Draws the candle plot on a GDI+ surface agains the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			CandleDataAdapter cd = new CandleDataAdapter( this.DataSource, this.DataMember, 
+				this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );
+
+			Brush bearishBrush = new SolidBrush( BearishColor );
+			Brush bullishBrush = new SolidBrush( BullishColor );
+
+            uint offset = 0;
+            if (this.centered_)
+            {
+                offset = (uint)(CalculatePhysicalSeparation(cd,xAxis) / 2.0f);
+            }
+
+            uint addAmount = (uint)StickWidth/2;
+			uint stickWidth = (uint)StickWidth;
+
+			if (StickWidth == AutoScaleStickWidth)
+			{
+				// default
+				addAmount = 2;
+				stickWidth = 4;
+            
+                float minDist = CalculatePhysicalSeparation( cd, xAxis );
+
+                addAmount = (uint)(minDist / 3);
+                stickWidth = addAmount * 2;
+            }
+
+			Pen	p =	new	Pen(this.color_);
+
+			/*
+			// brant hyatt proposed.
+			if (this.Style == Styles.Stick)
+			{
+				p.Width = stickWidth;
+ 				addAmount = stickWidth + 2;
+			}
+			*/
+
+			for (int i=0; i<cd.Count; ++i)
+			{
+
+				PointOLHC point = (PointOLHC)cd[i];
+				if ( (!double.IsNaN (point.Open)) && (!double.IsNaN(point.High)) && (!double.IsNaN (point.Low)) && (!double.IsNaN(point.Close)) )
+				{
+
+					int xPos = (int)(xAxis.WorldToPhysical( point.X, false )).X;
+
+					if (xPos + offset + addAmount < xAxis.PhysicalMin.X || xAxis.PhysicalMax.X < xPos + offset - addAmount)
+						continue;
+
+					int yPos1 = (int)(yAxis.WorldToPhysical( point.Low, false )).Y;
+					int yPos2 = (int)(yAxis.WorldToPhysical( point.High, false )).Y;
+					int yPos3 = (int)(yAxis.WorldToPhysical( point.Open, false )).Y;
+					int yPos4 = (int)(yAxis.WorldToPhysical( point.Close, false )).Y;
+
+					if (this.Style == Styles.Stick)
+					{
+
+						/*
+						// brant hyatt proposed.
+						if (i > 0) 
+						{
+							if ( ((PointOLHC)cd[i]).Close > ((PointOLHC)cd[i-1]).Close)
+							{
+								p.Color = BullishColor;
+							}
+							else
+							{
+								p.Color = BearishColor;
+							}
+						}
+						*/
+
+						g.DrawLine( p, xPos+offset, yPos1, xPos+offset, yPos2 );
+						g.DrawLine( p, xPos-addAmount+offset, yPos3, xPos+offset, yPos3 );
+						g.DrawLine( p, xPos+offset, yPos4, xPos+addAmount+offset, yPos4 );
+					}
+
+					else if (this.Style == Styles.Filled)
+					{
+						g.DrawLine( p, xPos+offset, yPos1, xPos+offset, yPos2 );
+						if (yPos3 > yPos4)
+						{
+							g.FillRectangle( bullishBrush, xPos-addAmount+offset, yPos4, stickWidth, yPos3 - yPos4 );
+							g.DrawRectangle( p, xPos-addAmount+offset, yPos4, stickWidth, yPos3 - yPos4 );
+						}
+						else if (yPos3 < yPos4)
+						{
+							g.FillRectangle( bearishBrush, xPos-addAmount+offset, yPos3, stickWidth, yPos4 - yPos3 );
+							g.DrawRectangle( p, xPos-addAmount+offset, yPos3, stickWidth, yPos4 - yPos3 );
+						}
+						else
+						{
+							g.DrawLine( p, xPos-addAmount+offset, yPos3, xPos-addAmount+stickWidth+offset, yPos3 );
+						}
+
+					}
+
+				}
+			}
+
+		}
+
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			CandleDataAdapter candleData = new CandleDataAdapter( this.DataSource, this.DataMember, 
+				this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );
+		
+			return candleData.SuggestXAxis();
+		}
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+			CandleDataAdapter candleData = new CandleDataAdapter( this.DataSource, this.DataMember, 
+				this.AbscissaData, this.OpenData, this.LowData, this.HighData, this.CloseData );
+
+			return candleData.SuggestYAxis();
+		}
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the open values.
+		/// </summary>
+		public object OpenData
+		{
+			get
+			{
+				return openData_;
+			}
+			set
+			{
+				openData_ = value;
+			}
+		}
+		private object openData_ = null;
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the interval low values.
+		/// </summary>
+		public object LowData
+		{
+			get
+			{
+				return lowData_;
+			}
+			set
+			{
+				lowData_ = value;
+			}
+		}
+		private object lowData_ = null;
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the interval high values.
+		/// </summary>
+		public object HighData
+		{
+			get
+			{
+				return highData_;
+			}
+			set
+			{
+				highData_ = value;
+			}
+		}
+		private object highData_ = null;
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the close values.
+		/// </summary>
+		public object CloseData
+		{
+			get
+			{
+				return closeData_;
+			}
+			set
+			{
+				closeData_ = value;
+			}
+		}	
+		private object closeData_ = null;
+
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the abscissa [x] axis.
+		/// </summary>
+		public object AbscissaData
+		{
+			get
+			{
+				return abscissaData_;
+			}
+			set
+			{
+				abscissaData_ = value;
+			}
+		}
+		private object abscissaData_ = null;
+
+
+		/// <summary>
+		/// Draws a representation of this plot in the legend.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public virtual void DrawInLegend( Graphics g, Rectangle startEnd )
+		{
+			Pen	p =	new	Pen(this.color_);
+
+			g.DrawLine( p, startEnd.Left, (startEnd.Top + startEnd.Bottom)/2, 
+				startEnd.Right, (startEnd.Top + startEnd.Bottom)/2 );
+
+		}
+
+
+		/// <summary>
+		/// Color of this plot [excluding interior of filled boxes if Style is fill]. To
+		/// change the Bullish and Bearish colours in Filled mode, use the BullishColor
+		/// and BearishColor properties.
+		/// </summary>
+		public System.Drawing.Color Color
+		{
+			get
+			{
+				return color_;
+			}
+			set
+			{
+				color_ = value;
+			}
+		}
+		Color color_ = Color.Black;
+		
+
+		/// <summary>
+		/// Possible CandleStick styles.
+		/// </summary>
+		public enum Styles
+		{
+
+			/// <summary>
+			/// Draw vertical line between low and high, tick on left for open and tick on right for close.
+			/// </summary>
+			Stick,
+
+			/// <summary>
+			/// Draw vertical line between low and high and place on top of this a box with bottom
+			/// and top determined by open and high values. The box is filled using the colors specified
+			/// in BullishColor and BearishColor properties.
+			/// </summary>
+			Filled
+
+		}
+
+
+		/// <summary>
+		/// Specifies the CandleStick style to use.
+		/// </summary>
+		public Styles Style = Styles.Filled;
+
+
+		/// <summary>
+		/// If CandlePlot.Style is Filled, then bullish open-close moves are displayed in this color.
+		/// </summary>
+		public Color BullishColor = Color.White;
+
+
+		/// <summary>
+		/// If CandlePlot.Style is Filled, then bearish moves are displayed in this color.
+		/// </summary>
+		public Color BearishColor = Color.Black;
+
+
+		/// <summary>
+		/// Width of each stick in pixels. It is best if this is an odd number.
+		/// </summary>
+		public int StickWidth
+		{
+			get
+			{
+				return stickWidth_;
+			}
+			set
+			{
+				if (value < 1)
+				{
+					throw new NPlotException( "Stick width must be greater than 0." );
+				}
+				stickWidth_ = value;
+			}
+		}
+		private int stickWidth_ = AutoScaleStickWidth;
+
+
+		/// <summary>
+		/// If stick width is set equal to this value, the width will be 
+		/// automatically scaled dependant on the space between sticks.
+		/// </summary>
+		public const int AutoScaleStickWidth = 0;
+
+        /// <summary>
+        /// If true (default), bars will be centered on the abscissa times. 
+        /// If false, bars will be drawn between the corresponding abscissa time
+        /// and the next abscissa time. 
+        /// </summary>
+        /// <value></value>
+        public bool Centered
+        {
+            get
+            {
+                return centered_;
+            }
+            set
+            {
+                centered_ = value;
+            }
+        }
+        private bool centered_ = true;
+
+
+        /// <summary>
+		/// Write data associated with the plot as text.
+		/// </summary>
+		/// <param name="sb">the string builder to write to.</param>
+		/// <param name="region">Only write out data in this region if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, only data in region is written, else all data is written.</param>
+		/// <remarks>TODO: not implemented.</remarks>
+		public void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion )
+		{
+		}
+
+	}
+}
diff --git a/nplot/nplot/DateTimeAxis.cs b/nplot/nplot/DateTimeAxis.cs
new file mode 100644
index 0000000..95292d9
--- /dev/null
+++ b/nplot/nplot/DateTimeAxis.cs
@@ -0,0 +1,702 @@
+/*
+NPlot - A charting library for .NET
+
+DateTimeAxis.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Collections;
+using System.Globalization;
+
+// TODO: More control over how labels are displayed.
+// TODO: SkipWeekends property.
+// TODO: Make a relative (as opposed to absolute) TimeAxis.
+
+namespace NPlot
+{
+	/// <summary>
+	/// The DateTimeAxis class
+	/// </summary>
+	public class DateTimeAxis : Axis
+	{
+
+		#region Clone implementation
+		/// <summary>
+		/// Deep copy of DateTimeAxis.
+		/// </summary>
+		/// <returns>A copy of the DateTimeAxis Class.</returns>
+		public override object Clone()
+		{
+			DateTimeAxis a = new DateTimeAxis();
+			// ensure that this isn't being called on a derived type. If it is, then oh no!
+			if (this.GetType() != a.GetType())
+			{
+				throw new NPlotException( "Clone not defined in derived type. Help!" );
+			}
+			DoClone( this, a );
+			return a;
+		}
+
+
+		/// <summary>
+		/// Helper method for Clone.
+		/// </summary>
+		/// <param name="a">The original object to clone.</param>
+		/// <param name="b">The cloned object.</param>
+		protected static void DoClone( DateTimeAxis b, DateTimeAxis a )
+		{
+			Axis.DoClone( b, a );
+		}
+		#endregion
+
+		private void Init()
+		{
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="a">Axis to construct from</param>
+		public DateTimeAxis( Axis a )
+			: base( a )
+		{
+			this.Init();
+			this.NumberFormat = null;
+		}
+
+
+		/// <summary>
+		/// Default Constructor
+		/// </summary>
+		public DateTimeAxis()
+			: base()
+		{
+			this.Init();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="worldMin">World min of axis</param>
+		/// <param name="worldMax">World max of axis</param>
+		public DateTimeAxis( double worldMin, double worldMax )
+			: base( worldMin, worldMax )
+		{
+			this.Init();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="worldMin">World min of axis</param>
+		/// <param name="worldMax">World max of axis</param>
+		public DateTimeAxis( long worldMin, long worldMax )
+			: base( (double)worldMin, (double)worldMax )
+		{
+			this.Init();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="worldMin">World min of axis</param>
+		/// <param name="worldMax">World max of axis</param>
+		public DateTimeAxis( DateTime worldMin, DateTime worldMax )
+			: base( (double)worldMin.Ticks, (double)worldMax.Ticks )
+		{
+			this.Init();
+		}
+
+
+		/// <summary>
+		/// Draw the ticks.
+		/// </summary>
+		/// <param name="g">The drawing surface on which to draw.</param>
+		/// <param name="physicalMin">The minimum physical extent of the axis.</param>
+		/// <param name="physicalMax">The maximum physical extent of the axis.</param>
+		/// <param name="boundingBox">out: smallest box that completely encompasses all of the ticks and tick labels.</param>
+		/// <param name="labelOffset">out: a suitable offset from the axis to draw the axis label.</param>
+		protected override void DrawTicks( 
+			Graphics g, 
+			Point physicalMin, 
+			Point physicalMax, 
+			out object labelOffset,
+			out object boundingBox )
+		{
+			
+			// TODO: Look at offset and bounding box logic again here. why temp and other vars? 
+
+			Point tLabelOffset;
+			Rectangle tBoundingBox;
+
+			labelOffset = this.getDefaultLabelOffset( physicalMin, physicalMax );
+			boundingBox = null;
+		
+			ArrayList largeTicks;
+			ArrayList smallTicks;
+			this.WorldTickPositions( physicalMin, physicalMax, out largeTicks, out smallTicks );
+
+			// draw small ticks.
+			for (int i=0; i<smallTicks.Count; ++i)
+			{
+				this.DrawTick( g, (double)smallTicks[i], 
+					this.SmallTickSize, "", new Point(0, 0),
+					physicalMin, physicalMax, 
+					out tLabelOffset, out tBoundingBox );
+				// assume label offset and bounding box unchanged by small tick bounds.
+			}
+
+			// draw large ticks.
+			for (int i=0; i<largeTicks.Count; ++i)
+			{
+					
+				DateTime tickDate = new DateTime( (long)((double)largeTicks[i]) );
+                string label = LargeTickLabel(tickDate);
+
+                this.DrawTick( g, (double)largeTicks[i],
+	                            this.LargeTickSize, label, new Point( 0, 0 ),
+                                physicalMin, physicalMax, out tLabelOffset, out tBoundingBox );
+
+                Axis.UpdateOffsetAndBounds( ref labelOffset, ref boundingBox, tLabelOffset, tBoundingBox );
+             }
+
+        }
+
+		/// <summary>
+		/// Get the label corresponding to the provided date time
+		/// </summary>
+		/// <param name="tickDate">the date time to get the label for</param>
+		/// <returns>label for the provided DateTime</returns>
+		protected virtual string LargeTickLabel(DateTime tickDate)
+		{
+			string label = "";
+
+			if(this.NumberFormat == null || this.NumberFormat == String.Empty) 
+			{
+				if ( this.LargeTickLabelType_ == LargeTickLabelType.year )
+				{
+					label = tickDate.Year.ToString();
+				}
+
+				else if ( this.LargeTickLabelType_ == LargeTickLabelType.month )
+				{
+					label = tickDate.ToString("MMM");
+					label += " ";
+					label += tickDate.Year.ToString().Substring(2,2);
+				}
+
+				else if ( this.LargeTickLabelType_ == LargeTickLabelType.day )
+				{
+					label = (tickDate.Day).ToString();
+					label += " ";
+					label += tickDate.ToString("MMM");
+				}
+				
+				else if ( this.LargeTickLabelType_ == LargeTickLabelType.hourMinute )
+				{
+					string minutes = tickDate.Minute.ToString();
+					if (minutes.Length == 1)
+					{
+						minutes = "0" + minutes;
+					}
+					label = tickDate.Hour.ToString() + ":" + minutes;
+				}
+				else if ( this.LargeTickLabelType_ == LargeTickLabelType.hourMinuteSeconds )
+				{
+					string minutes = tickDate.Minute.ToString();
+					string seconds = tickDate.Second.ToString();
+					if (seconds.Length == 1)
+					{
+						seconds = "0" + seconds;
+					}
+
+					if (minutes.Length == 1)
+					{
+						minutes = "0" + minutes;
+					}
+					label = tickDate.Hour.ToString() + ":" + minutes + "." + seconds;	
+				}
+
+			}
+			else 
+			{
+				label = tickDate.ToString(NumberFormat);
+			}
+
+			return label;
+		}
+
+
+		/// <summary>
+		/// Enumerates the different types of tick label possible.
+		/// </summary>
+		protected enum LargeTickLabelType 
+		{
+			/// <summary>
+			/// default - no tick labels.
+			/// </summary>
+			none = 0,
+
+			/// <summary>
+			/// tick labels should be years
+			/// </summary>
+			year = 1,
+
+			/// <summary>
+			/// Tick labels should be month names
+			/// </summary>
+			month = 2,
+
+			/// <summary>
+			/// Tick labels should be day names
+			/// </summary>
+			day = 3,
+
+			/// <summary>
+			/// Tick labels should be hour / minutes.
+			/// </summary>
+			hourMinute = 4,
+
+			/// <summary>
+			/// tick labels should be hour / minute / second.
+			/// </summary>
+			hourMinuteSeconds = 5
+		}
+
+
+		/// <summary>
+		///  this gets set after a get LargeTickPositions.
+		/// </summary>
+		protected LargeTickLabelType LargeTickLabelType_;
+
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the large ticks. No
+		/// small tick marks are currently calculated by this method.
+		/// 
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">null</param>
+		internal override void WorldTickPositions_FirstPass(
+			Point physicalMin, 
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+			smallTickPositions = null;
+
+			largeTickPositions = new ArrayList();
+
+			const int daysInMonth = 30;
+
+			TimeSpan timeLength = new TimeSpan( (long)(WorldMax-WorldMin));
+			DateTime worldMinDate = new DateTime( (long)this.WorldMin );
+			DateTime worldMaxDate = new DateTime( (long)this.WorldMax );
+
+			if(largeTickStep_ == TimeSpan.Zero) 
+			{
+
+				// if less than 10 minutes, then large ticks on second spacings. 
+
+				if ( timeLength < new TimeSpan(0,0,2,0,0) )
+				{
+					this.LargeTickLabelType_ = LargeTickLabelType.hourMinuteSeconds;
+
+					double secondsSkip;
+
+					if (timeLength < new TimeSpan( 0,0,0,10,0 ) )
+						secondsSkip = 1.0;
+					else if ( timeLength < new TimeSpan(0,0,0,20,0) )
+						secondsSkip = 2.0;
+					else if ( timeLength < new TimeSpan(0,0,0,50,0) )
+						secondsSkip = 5.0;
+					else if ( timeLength < new TimeSpan(0,0,2,30,0) )
+						secondsSkip = 15.0;
+					else 
+						secondsSkip = 30.0;
+
+					int second = worldMinDate.Second;
+					second -= second % (int)secondsSkip;					
+
+					DateTime currentTickDate = new DateTime( 
+						worldMinDate.Year,
+						worldMinDate.Month, 
+						worldMinDate.Day,
+						worldMinDate.Hour,
+						worldMinDate.Minute,
+						second,0 );
+
+					while ( currentTickDate < worldMaxDate )
+					{
+						double world = (double)currentTickDate.Ticks;
+
+						if ( world >= this.WorldMin && world <= this.WorldMax )
+						{
+							largeTickPositions.Add( world );
+						}
+
+						currentTickDate = currentTickDate.AddSeconds( secondsSkip );
+					}
+				}
+
+				// Less than 2 hours, then large ticks on minute spacings.
+
+				else if ( timeLength < new TimeSpan(0,2,0,0,0) )
+				{
+					this.LargeTickLabelType_ = LargeTickLabelType.hourMinute;
+
+					double minuteSkip;
+
+					if ( timeLength < new TimeSpan(0,0,10,0,0) )
+						minuteSkip = 1.0;
+					else if ( timeLength < new TimeSpan(0,0,20,0,0) )
+						minuteSkip = 2.0;
+					else if ( timeLength < new TimeSpan(0,0,50,0,0) )
+						minuteSkip = 5.0;
+					else if ( timeLength < new TimeSpan(0,2,30,0,0) )
+						minuteSkip = 15.0;
+					else //( timeLength < new TimeSpan( 0,5,0,0,0) )
+						minuteSkip = 30.0;
+
+					int minute = worldMinDate.Minute;
+					minute -= minute % (int)minuteSkip;					
+
+					DateTime currentTickDate = new DateTime( 
+						worldMinDate.Year,
+						worldMinDate.Month, 
+						worldMinDate.Day,
+						worldMinDate.Hour,
+						minute,0,0 );
+
+					while ( currentTickDate < worldMaxDate )
+					{
+						double world = (double)currentTickDate.Ticks;
+
+						if ( world >= this.WorldMin && world <= this.WorldMax )
+						{
+							largeTickPositions.Add( world );
+						}
+
+						currentTickDate = currentTickDate.AddMinutes( minuteSkip );
+					}
+				}
+
+				// Less than 2 days, then large ticks on hour spacings.
+
+				else if ( timeLength < new TimeSpan(2,0,0,0,0) )
+				{
+					this.LargeTickLabelType_ = LargeTickLabelType.hourMinute;
+
+					double hourSkip;
+					if ( timeLength < new TimeSpan(0,10,0,0,0) )
+						hourSkip = 1.0;
+					else if ( timeLength < new TimeSpan(0,20,0,0,0) )
+						hourSkip = 2.0;
+					else
+						hourSkip = 6.0;
+
+
+					int hour = worldMinDate.Hour;
+					hour -= hour % (int)hourSkip;					
+
+					DateTime currentTickDate = new DateTime( 
+						worldMinDate.Year,
+						worldMinDate.Month, 
+						worldMinDate.Day,
+						hour,0,0,0 );
+
+					while ( currentTickDate < worldMaxDate )
+					{
+						double world = (double)currentTickDate.Ticks;
+
+						if ( world >= this.WorldMin && world <= this.WorldMax )
+						{
+							largeTickPositions.Add( world );
+						}
+
+						currentTickDate = currentTickDate.AddHours( hourSkip );
+					}
+
+				}
+
+
+				// less than 5 months, then large ticks on day spacings.
+
+				else if ( timeLength < new TimeSpan(daysInMonth*4,0,0,0,0))
+				{
+					this.LargeTickLabelType_ = LargeTickLabelType.day;
+
+					double daySkip;
+					if ( timeLength < new TimeSpan(10,0,0,0,0) )
+						daySkip = 1.0;
+					else if (timeLength < new TimeSpan(20,0,0,0,0) )
+						daySkip = 2.0;
+					else if (timeLength < new TimeSpan(7*10,0,0,0,0) )
+						daySkip = 7.0;
+					else 
+						daySkip = 14.0;
+
+					DateTime currentTickDate = new DateTime( 
+						worldMinDate.Year,
+						worldMinDate.Month, 
+						worldMinDate.Day );
+
+                    if (daySkip == 2.0)
+                    {
+
+                        TimeSpan timeSinceBeginning = currentTickDate - DateTime.MinValue;
+
+                        if (timeSinceBeginning.Days % 2 == 1)
+                            currentTickDate = currentTickDate.AddDays(-1.0);
+                    }
+
+                    if (daySkip == 7 || daySkip == 14.0)
+                    {
+                        DayOfWeek dow = currentTickDate.DayOfWeek;
+                        switch (dow)
+                        {
+                            case DayOfWeek.Monday:
+                                break;
+                            case DayOfWeek.Tuesday:
+                                currentTickDate = currentTickDate.AddDays(-1.0);
+                                break;
+                            case DayOfWeek.Wednesday:
+                                currentTickDate = currentTickDate.AddDays(-2.0);
+                                break;
+                            case DayOfWeek.Thursday:
+                                currentTickDate = currentTickDate.AddDays(-3.0);
+                                break;
+                            case DayOfWeek.Friday:
+                                currentTickDate = currentTickDate.AddDays(-4.0);
+                                break;
+                            case DayOfWeek.Saturday:
+                                currentTickDate = currentTickDate.AddDays(-5.0);
+                                break;
+                            case DayOfWeek.Sunday:
+                                currentTickDate = currentTickDate.AddDays(-6.0);
+                                break;
+                        }
+
+                    }
+
+                    if (daySkip == 14.0f)
+                    {
+                        TimeSpan timeSinceBeginning = currentTickDate - DateTime.MinValue;
+
+                        if ((timeSinceBeginning.Days / 7) % 2 == 1)
+                        {
+                            currentTickDate = currentTickDate.AddDays(-7.0);
+                        }
+                    }
+
+                    while ( currentTickDate < worldMaxDate )
+					{
+						double world = (double)currentTickDate.Ticks;
+
+						if ( world >= this.WorldMin && world <= this.WorldMax )
+						{
+							largeTickPositions.Add( world );
+						}
+
+						currentTickDate = currentTickDate.AddDays(daySkip);
+					}
+				}
+
+
+					// else ticks on month or year spacings.
+
+				else if ( timeLength >= new TimeSpan(daysInMonth*4,0,0,0,0) )
+				{
+
+					int monthSpacing = 0;
+			
+					if ( timeLength.Days < daysInMonth*(12*3+6) )
+					{
+						LargeTickLabelType_ = LargeTickLabelType.month;
+
+						if ( timeLength.Days < daysInMonth*10 )
+							monthSpacing = 1;
+						else if ( timeLength.Days < daysInMonth*(12*2) )
+							monthSpacing = 3;
+						else // if ( timeLength.Days < daysInMonth*(12*3+6) )
+							monthSpacing = 6;
+					}
+					else
+					{
+						LargeTickLabelType_ = LargeTickLabelType.year;
+
+                        if (timeLength.Days < daysInMonth * (12 * 6))
+                            monthSpacing = 12;
+                        else if (timeLength.Days < daysInMonth * (12 * 12))
+                            monthSpacing = 24;
+                        else if (timeLength.Days < daysInMonth * (12 * 30))
+                            monthSpacing = 60;
+                        else
+                            monthSpacing = 120;
+                        //LargeTickLabelType_ = LargeTickLabelType.none;
+					}
+
+					// truncate start
+					DateTime currentTickDate = new DateTime( 
+						worldMinDate.Year,
+						worldMinDate.Month, 
+						1 );
+			
+					if (monthSpacing > 1)
+					{
+						currentTickDate = currentTickDate.AddMonths(
+							-(currentTickDate.Month-1)%monthSpacing );
+					}
+
+					// Align on 2 or 5 year boundaries if necessary.
+					if (monthSpacing >= 24)
+					{
+						currentTickDate = currentTickDate.AddYears(
+							-(currentTickDate.Year)%(monthSpacing/12) );						
+					}
+
+					//this.firstLargeTick_ = (double)currentTickDate.Ticks;
+
+					if ( LargeTickLabelType_ != LargeTickLabelType.none )
+					{
+						while ( currentTickDate < worldMaxDate )
+						{
+							double world = (double)currentTickDate.Ticks;
+
+							if ( world >= this.WorldMin && world <= this.WorldMax )
+							{
+								largeTickPositions.Add( world );
+							}
+
+							currentTickDate = currentTickDate.AddMonths( monthSpacing );
+						}
+					}
+				}
+			}
+			else
+			{
+				for (DateTime date = worldMinDate; date < worldMaxDate; date += largeTickStep_) 
+				{
+					largeTickPositions.Add((double)date.Ticks);
+				}
+			}
+        }
+
+
+        /// <summary>
+        /// Compute the small tick positions for largetick size of one or more years.
+        ///  - inside the domain or the large tick positons, is take the mid-point of pairs of large ticks
+        ///  - outside the large tick range, check if a half tick is inside the world min/max
+        /// This method works only if there are atleast 2 large ticks,
+        /// since we don't know if its minutes, hours, month, or yearly divisor.
+        /// </summary>
+        /// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+        /// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+        /// <param name="largeTickPositions">Read in the large tick positions</param>
+        /// <param name="smallTickPositions">Fill in the corresponding small tick positions</param>
+        /// <remarks>Added by Rosco Hill</remarks>
+        internal override void WorldTickPositions_SecondPass(
+            Point physicalMin,
+            Point physicalMax,
+            ArrayList largeTickPositions,
+            ref ArrayList smallTickPositions
+          )
+        {
+            if (largeTickPositions.Count < 2 || !(LargeTickLabelType_.Equals(LargeTickLabelType.year)))
+            {
+                smallTickPositions = new ArrayList(); ;
+            }
+            else
+            {
+                smallTickPositions = new ArrayList();
+                double diff = 0.5 * (((double)largeTickPositions[1]) - ((double)largeTickPositions[0]));
+                if (((double)largeTickPositions[0] - diff) > this.WorldMin)
+                {
+                    smallTickPositions.Add((double)largeTickPositions[0] - diff);
+                }
+                for (int i = 0; i < largeTickPositions.Count - 1; i++)
+                {
+                    smallTickPositions.Add(((double)largeTickPositions[i]) + diff);
+                }
+                if (((double)largeTickPositions[largeTickPositions.Count - 1] + diff) < this.WorldMax)
+                {
+                    smallTickPositions.Add((double)largeTickPositions[largeTickPositions.Count - 1] + diff);
+                }
+            }
+        }
+
+
+        /// <summary>
+		/// The distance between large ticks. If this is set to Zero [default],
+		/// this distance will be calculated automatically.
+		/// </summary>
+		public TimeSpan LargeTickStep 
+		{
+			set 
+			{
+				largeTickStep_ = value;
+			}
+			get 
+			{
+				return largeTickStep_;
+			}
+		}
+ 		private TimeSpan largeTickStep_ = TimeSpan.Zero;
+
+
+	}
+}
diff --git a/nplot/nplot/ErrorHandler.cs b/nplot/nplot/ErrorHandler.cs
new file mode 100644
index 0000000..ef2181a
--- /dev/null
+++ b/nplot/nplot/ErrorHandler.cs
@@ -0,0 +1,179 @@
+/*
+NPlot - A charting library for .NET
+
+ErrorHandler.cs
+Copyright (C) 2004
+Matt Howlett
+
+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 following text in 
+   the documentation and / or other materials provided with the 
+   distribution: 
+   
+   "This product includes software developed as part of 
+   the NPlot charting library project available from: 
+   http://www.nplot.com/"; 
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+$Id: ErrorHandler.cs,v 1.2 2004/11/04 12:18:24 mhowlett Exp $
+
+*/
+
+using System;
+using System.Diagnostics;
+
+
+namespace NPlot
+{
+	/// <summary>
+	/// Error class that 
+	/// (a) adds source of problem. 
+	/// (b) by default throws exception.
+	/// (c) default behaviour can be overridden.
+	/// </summary>
+	public class ErrorHandler
+	{
+
+		/// <summary>
+		/// The singular ErrorHandler instance 
+		/// </summary>
+		public static readonly ErrorHandler Instance = new ErrorHandler();
+
+		/// <summary>
+		/// Defines the allowed message levels. 
+		/// </summary>
+		public enum Level
+		{
+			/// <summary>
+			/// Debugging Message. Note it's best not to use the general 
+			/// handle method to emit these - use the conditional Debug
+			/// function.
+			/// </summary>
+			Debug = 0,
+			/// <summary>
+			/// Something went wrong, but code execution can continue.
+			/// </summary>
+			Continuing = 1,
+			/// <summary>
+			/// Something wend wrong, and code execution can not continue.
+			/// </summary>
+			Critical = 2
+		}
+
+		private delegate void HandlerDelegate( string message );
+
+		private HandlerDelegate[] handlers_;
+
+		private void HandleError( string message )
+		{
+			throw new System.Exception( message );
+		}
+
+		private void NullHandler( string message )
+		{
+			// do nothing.
+		}
+
+		private ErrorHandler()
+		{
+			// set up default 
+			handlers_ = new HandlerDelegate[3];
+			handlers_[0] = new HandlerDelegate(HandleError);
+			handlers_[1] = new HandlerDelegate(HandleError);
+			handlers_[2] = new HandlerDelegate(HandleError);
+		}
+
+		/// <summary>
+		/// Handles a debug message.
+		/// </summary>
+		/// <param name="message">message to handle</param>
+		[Conditional("DEBUG")]
+		public void DebugError( string message )
+		{
+			handlers_[(int)Level.Debug]( GetPrepend() + message );
+		}
+
+
+		/// <summary>
+		/// Handles a continuing error.
+		/// </summary>
+		/// <param name="message">message to handle</param>
+		public void ContinuingError( string message ) 
+		{
+			handlers_[(int)Level.Continuing]( GetPrepend() + message );
+		}
+
+
+		/// <summary>
+		/// Handles a critical error.
+		/// </summary>
+		/// <param name="message">message to handle</param>
+		public void CriticalError( string message )
+		{
+			handlers_[(int)Level.Critical]( GetPrepend() + message );
+		}
+
+
+		/// <summary>
+		/// Handles a message of the given level.
+		/// </summary>
+		/// <param name="level">The message level.</param>
+		/// <param name="message">The message to handle.</param>
+		public void Handle( Level level, string message )
+		{
+			handlers_[(int)level]( GetPrepend() + message );
+		}
+
+
+		/// <summary>
+		/// Get text to prepend to log message to describe its origin. This is the first
+		/// place in stack outside Cts.Library.MessageLog.
+		/// </summary>
+		/// <returns>string to prepend to messages detailing its origin.</returns>
+		private string GetPrepend()
+		{
+			System.Diagnostics.StackTrace st = new StackTrace();
+			System.Diagnostics.StackFrame sf = null;
+			string prepend = "";
+			int i = 0;
+			while (i < st.FrameCount)
+			{
+				sf = st.GetFrame(i);
+				if ( sf.GetMethod().ReflectedType != this.GetType() )
+				{
+					break;
+				}
+				i += 1;
+			}
+
+			sf = st.GetFrame(i);
+			prepend = 
+				"[" +
+				sf.GetMethod().ReflectedType +
+				"." +
+				sf.GetMethod().Name +
+				"] ";
+			return prepend;
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/FilledRegion.cs b/nplot/nplot/FilledRegion.cs
new file mode 100644
index 0000000..51ae1cb
--- /dev/null
+++ b/nplot/nplot/FilledRegion.cs
@@ -0,0 +1,203 @@
+/*
+NPlot - A charting library for .NET
+
+FilledRegion.cs
+Copyright (C) 2004
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// A quick and dirty Filled region plottable object
+	/// </summary>
+	public class FilledRegion : IDrawable
+	{
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="lp1">LinePlot that provides bounds to filled region [upper or lower]</param>
+		/// <param name="lp2">LinePlot that provides bounds to filled region [upper or lower]</param>
+		/// <remarks>TODO: make this work with other plot types.</remarks>
+		public FilledRegion( LinePlot lp1, LinePlot lp2 )
+		{
+			lp1_ = lp1;
+			lp2_ = lp2;
+		}
+
+
+        /// <summary>
+        /// Constructor
+        /// </summary>
+        /// <param name="l1">Vertical line to provide bounds for filled region</param>
+        /// <param name="l2">The other Vertical line to provide bounds for filled region</param>
+        public FilledRegion(VerticalLine l1, VerticalLine l2)
+        {
+            vl1_ = l1;
+            vl2_ = l2;
+        }
+
+
+        /// <summary>
+        /// Constructor
+        /// </summary>
+        /// <param name="l1">Vertical line to provide bounds for filled region</param>
+        /// <param name="l2">The other Vertical line to provide bounds for filled region</param>
+        public FilledRegion(HorizontalLine l1, HorizontalLine l2)
+        {
+            hl1_ = l1;
+            hl2_ = l2;
+        }
+
+
+        /// <summary>
+		/// Draw the filled region
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+            ITransform2D t = Transform2D.GetTransformer(xAxis, yAxis);
+
+            Brush b = brush_;
+            if (b == null)
+            {
+                b = areaBrush_.Get(new Rectangle(xAxis.PhysicalMin.X, yAxis.PhysicalMax.Y, xAxis.PhysicalLength, yAxis.PhysicalLength));
+            }
+
+            if (hl1_ != null && hl2_ != null)
+            {
+                PointF[] points = new PointF[4];
+                points[0] = t.Transform(xAxis.Axis.WorldMin, hl1_.OrdinateValue);
+                points[1] = t.Transform(xAxis.Axis.WorldMax, hl1_.OrdinateValue);
+                points[2] = t.Transform(xAxis.Axis.WorldMax, hl2_.OrdinateValue);
+                points[3] = t.Transform(xAxis.Axis.WorldMin, hl2_.OrdinateValue);
+
+                g.FillPolygon(b, points);
+            }   
+            else if (vl1_ != null && vl2_ != null)
+            {
+                PointF[] points = new PointF[4];
+                points[0] = t.Transform(vl1_.AbscissaValue, yAxis.Axis.WorldMin);
+                points[1] = t.Transform(vl1_.AbscissaValue, yAxis.Axis.WorldMax);
+                points[2] = t.Transform(vl2_.AbscissaValue, yAxis.Axis.WorldMax);
+                points[3] = t.Transform(vl2_.AbscissaValue, yAxis.Axis.WorldMin);
+
+                g.FillPolygon(b, points);
+            }
+            else if (lp1_ != null && lp2_ != null)
+            {
+
+                SequenceAdapter a1 = new SequenceAdapter(lp1_.DataSource, lp1_.DataMember, lp1_.OrdinateData, lp1_.AbscissaData);
+                SequenceAdapter a2 = new SequenceAdapter(lp2_.DataSource, lp2_.DataMember, lp2_.OrdinateData, lp2_.AbscissaData);
+
+
+                int count = a1.Count + a2.Count;
+                PointF[] points = new PointF[count];
+                for (int i = 0; i < a1.Count; ++i)
+                {
+                    points[i] = t.Transform(a1[i]);
+                }
+                for (int i = 0; i < a2.Count; ++i)
+                {
+                    points[i + a1.Count] = t.Transform(a2[a2.Count - i - 1]);
+                }
+
+                g.FillPolygon(b, points);
+            }
+            else
+            {
+                throw new NPlotException("One of bounds was set to null");
+            }
+        }
+
+
+		/// <summary>
+		/// Use this brush (and not a RectangleBrush) for drawing.
+		/// </summary>
+		public Brush Brush
+		{
+			set
+			{
+				brush_ = value;
+				areaBrush_ = null;
+			}
+		}
+
+
+		/// <summary>
+		/// Use this RectangleBrush (and not a normal Brush) for drawing.
+		/// </summary>
+		public IRectangleBrush RectangleBrush
+		{
+			set
+			{
+				brush_ = null;
+				areaBrush_ = value;
+			}
+		}
+
+
+        private VerticalLine vl1_;
+        private VerticalLine vl2_;
+
+        private HorizontalLine hl1_;
+        private HorizontalLine hl2_;
+
+        private LinePlot lp1_;
+		private LinePlot lp2_;
+
+		private Brush brush_ = new SolidBrush( Color.GhostWhite );
+		private IRectangleBrush areaBrush_ = null;
+	}
+
+}
diff --git a/nplot/nplot/FontScaler.cs b/nplot/nplot/FontScaler.cs
new file mode 100644
index 0000000..1295671
--- /dev/null
+++ b/nplot/nplot/FontScaler.cs
@@ -0,0 +1,46 @@
+/*
+NPlot - A charting library for .NET
+
+FontScaler.cs
+Copyright (C) 2003
+Matt Howlett
+
+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 following text in 
+   the documentation and / or other materials provided with the 
+   distribution: 
+   
+   "This product includes software developed as part of 
+   the NPlot charting library project available from: 
+   http://www.nplot.com/"; 
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+$Id: FontScaler.cs,v 1.10 2004/11/04 12:18:24 mhowlett Exp $
+
+*/
+
+using System;
+using System.Drawing;
+
+namespace NPlot
+{
+
+}
diff --git a/nplot/nplot/Grid.cs b/nplot/nplot/Grid.cs
new file mode 100644
index 0000000..02eb467
--- /dev/null
+++ b/nplot/nplot/Grid.cs
@@ -0,0 +1,248 @@
+/*
+NPlot - A charting library for .NET
+
+Grid.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+using System.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Encapsulates a Grid IDrawable object. Instances of this  to a PlotSurface2D 
+	/// instance to produce a grid.
+	/// </summary>
+	public class Grid : IDrawable
+	{
+
+		/// <summary>
+		/// 
+		/// </summary>
+		public enum GridType
+		{
+			/// <summary>
+			/// No grid.
+			/// </summary>
+			None = 0,
+			/// <summary>
+			/// Coarse grid. Lines at large tick positions only.
+			/// </summary>
+			Coarse = 1,
+			/// <summary>
+			/// Fine grid. Lines at both large and small tick positions.
+			/// </summary>
+			Fine = 2
+		}
+
+
+		/// <summary>
+		/// Default constructor
+		/// </summary>
+		public Grid()
+		{
+			minorGridPen_ = new Pen( Color.LightGray );
+			float[] pattern = {1.0f, 2.0f};
+			minorGridPen_.DashPattern = pattern;
+			
+			majorGridPen_ = new Pen( Color.LightGray );
+
+			horizontalGridType_ = GridType.Coarse;
+			
+			verticalGridType_ = GridType.Coarse;
+		}
+
+
+		/// <summary>
+		/// Specifies the horizontal grid type (none, coarse or fine).
+		/// </summary>
+		public GridType HorizontalGridType
+		{
+			get
+			{
+				return horizontalGridType_;
+			}
+			set
+			{
+				horizontalGridType_ = value;
+			}
+		}
+		GridType horizontalGridType_;
+
+
+		/// <summary>
+		/// Specifies the vertical grid type (none, coarse, or fine).
+		/// </summary>
+		public GridType VerticalGridType
+		{
+			get
+			{
+				return verticalGridType_;
+			}
+			set
+			{
+				verticalGridType_ = value;
+			}
+		}
+		GridType verticalGridType_;
+
+
+		/// <summary>
+		/// The pen used to draw major (coarse) grid lines.
+		/// </summary>
+		public System.Drawing.Pen MajorGridPen
+		{
+			get
+			{
+				return majorGridPen_;
+			}
+			set
+			{
+				majorGridPen_ = value;
+			}
+		}
+		private Pen majorGridPen_;
+
+
+		/// <summary>
+		/// The pen used to draw minor (fine) grid lines.
+		/// </summary>
+		public System.Drawing.Pen MinorGridPen
+		{
+			get
+			{
+				return minorGridPen_;
+			}
+			set
+			{
+				minorGridPen_ = value;
+			}
+		}
+		private Pen minorGridPen_;
+
+
+		/// <summary>
+		/// Does all the work in drawing grid lines.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to render.</param>
+		/// <param name="axis">TODO</param>
+		/// <param name="orthogonalAxis">TODO</param>
+		/// <param name="a">the list of world values to draw grid lines at.</param>
+		/// <param name="horizontal">true if want horizontal lines, false otherwise.</param>
+		/// <param name="p">the pen to use to draw the grid lines.</param>
+		private void DrawGridLines( 
+			Graphics g, PhysicalAxis axis, PhysicalAxis orthogonalAxis,
+			System.Collections.ArrayList a, bool horizontal, Pen p )
+		{
+			for (int i=0; i<a.Count; ++i)
+			{
+				PointF p1 = axis.WorldToPhysical((double)a[i], true);
+				PointF p2 = p1;
+				PointF p3 = orthogonalAxis.PhysicalMax;
+				PointF p4 = orthogonalAxis.PhysicalMin;
+				if (horizontal)
+				{
+					p1.Y = p4.Y;
+					p2.Y = p3.Y;
+				}
+				else
+				{
+					p1.X = p4.X;
+					p2.X = p3.X;
+				}
+				// note: casting all drawing was necessary for sane display. why?
+				g.DrawLine( p, (int)p1.X, (int)p1.Y, (int)p2.X, (int)p2.Y );
+			}
+		}
+
+		/// <summary>
+		/// Draws the grid
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw</param>
+		/// <param name="xAxis">The physical x axis to draw horizontal lines parallel to.</param>
+		/// <param name="yAxis">The physical y axis to draw vertical lines parallel to.</param>
+		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+
+			ArrayList xLargePositions = null;
+			ArrayList yLargePositions = null;
+			ArrayList xSmallPositions = null;
+			ArrayList ySmallPositions = null;
+
+			if (this.horizontalGridType_ != GridType.None)
+			{
+				xAxis.Axis.WorldTickPositions_FirstPass( xAxis.PhysicalMin, xAxis.PhysicalMax, out xLargePositions, out xSmallPositions );
+				DrawGridLines( g, xAxis, yAxis, xLargePositions, true, this.MajorGridPen );	
+			}
+
+			if (this.verticalGridType_ != GridType.None)
+			{
+				yAxis.Axis.WorldTickPositions_FirstPass( yAxis.PhysicalMin, yAxis.PhysicalMax, out yLargePositions, out ySmallPositions );
+				DrawGridLines( g, yAxis, xAxis, yLargePositions, false, this.MajorGridPen );
+			}
+
+
+			if (this.horizontalGridType_ == GridType.Fine)
+			{
+				xAxis.Axis.WorldTickPositions_SecondPass( xAxis.PhysicalMin, xAxis.PhysicalMax, xLargePositions, ref xSmallPositions );
+				DrawGridLines( g, xAxis, yAxis, xSmallPositions, true, this.MinorGridPen );
+			}
+
+			if (this.verticalGridType_ == GridType.Fine)
+			{
+				yAxis.Axis.WorldTickPositions_SecondPass( yAxis.PhysicalMin, yAxis.PhysicalMax, yLargePositions, ref ySmallPositions );
+				DrawGridLines( g, yAxis, xAxis, ySmallPositions, false, this.MinorGridPen );
+			}
+
+		}
+
+	}
+}
diff --git a/nplot/nplot/HistogramPlot.cs b/nplot/nplot/HistogramPlot.cs
new file mode 100644
index 0000000..406c99e
--- /dev/null
+++ b/nplot/nplot/HistogramPlot.cs
@@ -0,0 +1,521 @@
+/*
+NPlot - A charting library for .NET
+
+HistogramPlot.cs
+Copyright (C) 2003
+Matt Howlett, Paolo Pierini
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+using System.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Provides ability to draw histogram plots.
+	/// </summary>
+	public class HistogramPlot : BaseSequencePlot, IPlot, ISequencePlot
+	{
+
+		/// <summary>
+		/// Set/Get the brush to use if the histogram is filled.
+		/// </summary>
+		public IRectangleBrush RectangleBrush
+		{
+			get
+			{
+				return rectangleBrush_;
+			}
+			set
+			{
+				rectangleBrush_ = value;
+			}
+
+		}
+		private IRectangleBrush rectangleBrush_ = new RectangleBrushes.Solid( Color.Black );
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public HistogramPlot()
+		{
+		}
+
+
+		/// <summary>
+		/// Renders the histogram.
+		/// </summary>
+		/// <param name="g">The Graphics surface on which to draw</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			float yoff;
+
+			for ( int i=0; i<data.Count; ++i )
+			{
+
+				// (1) determine the top left hand point of the bar (assuming not centered)
+				PointD p1 = data[i];
+				if ( double.IsNaN(p1.X) || double.IsNaN(p1.Y) )
+					continue;
+				
+				// (2) determine the top right hand point of the bar (assuming not centered)
+				PointD p2;
+				if (i+1 != data.Count)
+				{
+					p2 = data[i+1];
+					if ( double.IsNaN(p2.X) || double.IsNaN(p2.Y) )
+						continue;
+					p2.Y = p1.Y;
+				}
+				else if (i != 0)
+				{
+					p2 = data[i-1];
+					if ( double.IsNaN(p2.X) || double.IsNaN(p2.Y) )
+						continue;
+					double offset = p1.X - p2.X;
+					p2.X = p1.X + offset;
+					p2.Y = p1.Y;
+				}
+				else
+				{
+					double offset = 1.0f;
+					p2.X = p1.X + offset;
+					p2.Y = p1.Y;
+				}
+
+				// (3) now account for plots this may be stacked on top of.
+				HistogramPlot currentPlot = this;
+				yoff = 0.0f;
+				double yval = 0.0f;
+				while (currentPlot.isStacked_)
+				{
+					SequenceAdapter stackedToData = new SequenceAdapter(
+						currentPlot.stackedTo_.DataSource, 
+						currentPlot.stackedTo_.DataMember,
+						currentPlot.stackedTo_.OrdinateData, 
+						currentPlot.stackedTo_.AbscissaData );
+
+					yval += stackedToData[i].Y;
+					yoff = yAxis.WorldToPhysical( yval, false ).Y;
+					p1.Y += stackedToData[i].Y;
+					p2.Y += stackedToData[i].Y;
+					currentPlot = currentPlot.stackedTo_;
+				}
+
+				// (4) now account for centering
+				if ( center_ )
+				{
+					double offset = ( p2.X - p1.X ) / 2.0f;
+					p1.X -= offset;
+					p2.X -= offset;
+				}
+
+				// (5) now account for BaseOffset (shift of bar sideways).
+                p1.X += baseOffset_;
+                p2.X += baseOffset_;
+
+				// (6) now get physical coordinates of top two points.
+                PointF xPos1 = xAxis.WorldToPhysical( p1.X, false );
+				PointF yPos1 = yAxis.WorldToPhysical( p1.Y, false );
+				PointF xPos2 = xAxis.WorldToPhysical( p2.X, false );
+				PointF yPos2 = yAxis.WorldToPhysical( p2.Y, false );
+
+				if (isStacked_)
+				{
+					currentPlot = this;
+					while (currentPlot.isStacked_)
+					{
+						currentPlot = currentPlot.stackedTo_;
+					}
+					this.baseWidth_ = currentPlot.baseWidth_;
+				}
+
+				float width = xPos2.X - xPos1.X;
+				float height;
+				if (isStacked_)
+				{
+					height = -yPos1.Y+yoff;
+				}
+				else
+				{
+					height = -yPos1.Y+yAxis.PhysicalMin.Y;
+				}
+
+				float xoff = (1.0f - baseWidth_)/2.0f*width;
+				Rectangle r = new Rectangle( (int)(xPos1.X+xoff), (int)yPos1.Y, (int)(width-2*xoff), (int)height );
+
+				if (this.Filled)
+				{
+					if (r.Height != 0 && r.Width != 0)
+					{
+						// room for optimization maybe.
+						g.FillRectangle( rectangleBrush_.Get(r), r );
+					}
+				}
+
+				g.DrawRectangle( Pen, r.X, r.Y, r.Width, r.Height );
+				
+			}
+		}
+
+
+		/// <summary>
+		/// Whether or not the histogram columns will be filled.
+		/// </summary>
+		public bool Filled
+		{
+			get 
+			{
+				return filled_;
+			}
+			set
+			{
+				filled_ = value;
+			}
+		}
+		private bool filled_ = false;
+
+
+		private float baseWidth_ = 1.0f;
+        /// <summary>
+        /// The width of the histogram bar as a proportion of the data spacing 
+        /// (in range 0.0 - 1.0).
+        /// </summary>
+		public float BaseWidth
+		{
+			get
+			{
+				return baseWidth_;
+			}
+			set
+			{
+				if (value > 0.0 && value <= 1.0)
+				{
+					baseWidth_ = value;
+				}
+				else
+				{
+					throw new NPlotException( "Base width must be between 0.0 and 1.0" );
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			Axis a = data.SuggestXAxis();
+
+			PointD p1;
+			PointD p2;
+			PointD p3;
+			PointD p4;
+			if (data.Count < 2)
+			{
+				p1 = data[0];
+				p1.X -= 1.0;
+				p2 = data[0];
+				p3 = p1;
+				p4 = p2;
+			}
+			else
+			{
+				p1 = data[0];
+				p2 = data[1];
+				p3 = data[data.Count-2];
+				p4 = data[data.Count-1];
+			}
+
+			double offset1;
+			double offset2;
+
+			if (!center_)
+			{
+				offset1 = 0.0f;
+				offset2 = p4.X - p3.X;
+			}
+			else
+			{
+				offset1 = (p2.X - p1.X)/2.0f;
+				offset2 = (p4.X - p3.X)/2.0f;
+			}
+
+			a.WorldMin -= offset1;
+			a.WorldMax += offset2;
+
+			return a;
+		}
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+
+			if ( this.isStacked_ )
+			{
+				double tmpMax = 0.0f;
+				ArrayList adapterList = new ArrayList();
+
+				HistogramPlot currentPlot = this;
+				do
+				{
+					adapterList.Add( new SequenceAdapter( 
+						currentPlot.DataSource,
+						currentPlot.DataMember,
+						currentPlot.OrdinateData, 
+						currentPlot.AbscissaData )
+					);
+				} while ((currentPlot = currentPlot.stackedTo_) != null);
+				
+				SequenceAdapter[] adapters =
+					(SequenceAdapter[])adapterList.ToArray(typeof(SequenceAdapter));
+				
+				for (int i=0; i<adapters[0].Count; ++i)
+				{
+					double tmpHeight = 0.0f;
+					for (int j=0; j<adapters.Length; ++j)
+					{
+						tmpHeight += adapters[j][i].Y;
+					}
+					tmpMax = Math.Max(tmpMax, tmpHeight);
+				}
+
+				Axis a = new LinearAxis(0.0f,tmpMax);
+				// TODO make 0.08 a parameter.
+				a.IncreaseRange( 0.08 );
+				return a;
+			}
+			else
+			{
+				SequenceAdapter data = 
+					new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+				return data.SuggestYAxis();
+			}
+		}
+
+
+		/*
+		private double centerLine_ = 0.0;
+		/// <summary>
+		/// Histogram bars extend from the data value to this value. Default value is 0.
+		/// </summary>
+		public double CenterLine
+		{
+			get
+			{
+				return centerLine_;
+			}
+			set
+			{
+				centerLine_ = value;
+			}
+		}
+		*/
+
+
+		private bool center_ = true;
+		/// <summary>
+		/// If true, each histogram column will be centered on the associated abscissa value.
+		/// If false, each histogram colum will be drawn between the associated abscissa value, and the next abscissa value.
+		/// Default value is true.
+		/// </summary>
+		public bool Center
+		{
+			set
+			{
+				center_ = value;
+			}
+			get
+			{
+				return center_;
+			}
+		}
+
+
+		/// <summary>
+		/// If this histogram plot has another stacked on top, this will be true. Else false.
+		/// </summary>
+		public bool IsStacked
+		{
+			get
+			{
+				return isStacked_;
+			}
+		}
+		private bool isStacked_;
+
+
+		private HistogramPlot stackedTo_;
+		/// <summary>
+		/// Stack the histogram to another HistogramPlot.
+		/// </summary>
+		public void StackedTo(HistogramPlot hp)
+		{
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			SequenceAdapter hpData = 
+				new SequenceAdapter( hp.DataSource, hp.DataMember, hp.OrdinateData, hp.AbscissaData );
+
+				if ( hp != null )
+				{
+					isStacked_ = true;
+					if ( hpData.Count != data.Count )
+					{
+						throw new NPlotException("Can stack HistogramPlot data only with the same number of datapoints.");
+					}
+					for ( int i=0; i < data.Count; ++i )
+					{
+						if ( data[i].X != hpData[i].X )
+						{
+							throw new NPlotException("Can stack HistogramPlot data only with the same X coordinates.");
+						}
+						if ( hpData[i].Y < 0.0f)
+						{
+							throw new NPlotException("Can stack HistogramPlot data only with positive Y coordinates.");
+						}
+					}
+				}
+				stackedTo_ = hp;
+		}
+
+
+		/// <summary>
+		/// Draws a representation of this plot in the legend.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public void DrawInLegend( Graphics g, Rectangle startEnd )
+		{
+	
+			if (Filled)
+			{
+				g.FillRectangle( rectangleBrush_.Get(startEnd), startEnd );
+			}
+
+			g.DrawRectangle( Pen, startEnd.X, startEnd.Y, startEnd.Width, startEnd.Height );
+
+		}
+
+
+        /// <summary>
+        /// The pen used to draw the plot
+        /// </summary>
+        public System.Drawing.Pen Pen
+        {
+            get
+            {
+                return pen_;
+            }
+            set
+            {
+                pen_ = value;
+            }
+        }
+        private System.Drawing.Pen pen_ = new Pen(Color.Black);
+
+
+        /// <summary>
+        /// The color of the pen used to draw lines in this plot.
+        /// </summary>
+        public System.Drawing.Color Color
+        {
+            set
+            {
+                if (pen_ != null)
+                {
+                    pen_.Color = value;
+                }
+                else
+                {
+                    pen_ = new Pen(value);
+                }
+            }
+            get
+            {
+                return pen_.Color;
+            }
+        }
+
+
+        /// <summary>
+        /// Horizontal position of histogram columns is offset by this much (in world coordinates).
+        /// </summary>
+        public double BaseOffset
+        {
+            set
+            {
+                baseOffset_ = value;
+            }
+            get
+            {
+                return baseOffset_;
+            }
+        }
+        private double baseOffset_;
+
+
+    }
+}
diff --git a/nplot/nplot/HorizontalLine.cs b/nplot/nplot/HorizontalLine.cs
new file mode 100644
index 0000000..c1e9486
--- /dev/null
+++ b/nplot/nplot/HorizontalLine.cs
@@ -0,0 +1,287 @@
+/*
+NPlot - A charting library for .NET
+
+HorizontalLine.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 drawing a horizontal line on a plot surface.
+	/// </summary>
+	public class HorizontalLine : IPlot
+	{
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="ordinateValue">ordinate (Y) value of line.</param>
+		public HorizontalLine( double ordinateValue )
+		{
+			this.value_ = ordinateValue;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="ordinateValue">ordinate (Y) value of line.</param>
+		/// <param name="color">draw the line using this color.</param>
+		public HorizontalLine( double ordinateValue, Color color )
+		{
+			this.value_ = ordinateValue;
+			this.pen_ = new Pen( color );
+		}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="ordinateValue">ordinate (Y) value of line.</param>
+		/// <param name="pen">Pen to use to draw the line.</param>
+		public HorizontalLine( double ordinateValue, Pen pen )
+		{
+			this.value_ = ordinateValue;
+			this.pen_ = pen;
+		}
+
+		
+		/// <summary>
+		/// Draws a representation of the horizontal line in the legend.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public void DrawInLegend(System.Drawing.Graphics g, System.Drawing.Rectangle startEnd)
+		{
+			g.DrawLine( pen_, startEnd.Left, (startEnd.Top + startEnd.Bottom)/2, 
+				startEnd.Right, (startEnd.Top + startEnd.Bottom)/2 );
+		}
+
+
+		/// <summary>
+		/// A label to associate with the plot - used in the legend.
+		/// </summary>
+		public string Label
+		{
+			get
+			{
+				return label_;
+			}
+			set
+			{
+				this.label_ = value;
+			}
+		}
+		
+		private string label_ = "";
+
+
+		/// <summary>
+		/// Whether or not to include an entry for this plot in the legend if it exists.
+		/// </summary>
+		public bool ShowInLegend
+		{
+			get
+			{
+				return showInLegend_;
+			}
+			set
+			{
+				this.showInLegend_ = value;
+			}
+		}
+		private bool showInLegend_ = false;
+
+
+		/// <summary>
+		/// Returns null indicating that x extremities of the line are variable.
+		/// </summary>
+		/// <returns>null</returns>
+		public Axis SuggestXAxis()
+		{
+			return null;
+		}
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+			return new LinearAxis( this.value_, this.value_ );
+		}
+
+		/// <summary>
+		/// Writes text data describing the horizontal line object to the supplied string builder. It is 
+		/// possible to specify that the data will be written only if the line is in the specified 
+		/// region.
+		/// </summary>
+		/// <param name="sb">the StringBuilder object to write to.</param>
+		/// <param name="region">a region used if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, data will be written only if the line is in the specified region.</param>
+		public void WriteData(System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion)
+		{
+
+			// return if line is not in plot region and 
+			if (value_ > region.Y+region.Height || value_ < region.Y)
+			{
+				if (onlyInRegion)
+				{
+					return;
+				}
+			}
+
+			sb.Append( "Label: " );
+			sb.Append( this.Label );
+			sb.Append( "\r\n" );
+			sb.Append( value_.ToString() );
+			sb.Append( "\r\n" );
+
+		}
+
+		/// <summary>
+		/// Draws the horizontal line plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis)
+		{
+			int xMin = xAxis.PhysicalMin.X;
+			int xMax = xAxis.PhysicalMax.X;
+			
+			xMin += pixelIndent_;
+			xMax -= pixelIndent_;
+
+			float length = Math.Abs(xMax - xMin);
+			float lengthDiff = length - length*scale_;
+			float indentAmount = lengthDiff/2;
+
+			xMin += (int)indentAmount;
+			xMax -= (int)indentAmount;
+
+			int yPos = (int)yAxis.WorldToPhysical( value_, false ).Y;
+		
+			g.DrawLine( pen_, new System.Drawing.Point( xMin, yPos ), new System.Drawing.Point( xMax, yPos ) );
+
+			// todo:  clip and proper logic for flipped axis min max.
+		}
+
+		private double value_;
+		/// <summary>
+		/// ordinate (Y) value to draw horizontal line at.
+		/// </summary>
+		public double OrdinateValue
+		{
+			get
+			{
+				return value_;
+			}
+			set
+			{
+				value_ = value;
+			}
+		}
+
+		private Pen pen_ = new Pen( Color.Black );
+		/// <summary>
+		/// Pen to use to draw the horizontal line.
+		/// </summary>
+		public Pen Pen
+		{
+			get
+			{
+				return pen_;
+			}
+			set
+			{
+				pen_ = value;
+			}
+		}
+
+		
+		/// <summary>
+		/// Each end of the line is indented by this many pixels. 
+		/// </summary>
+		public int PixelIndent
+		{
+			get
+			{
+				return pixelIndent_;
+			}
+			set
+			{
+				pixelIndent_ = value;
+			}
+		}
+		private int pixelIndent_ = 0;
+
+
+		/// <summary>
+		/// The line length is multiplied by this amount. Default
+		/// corresponds to a value of 1.0.
+		/// </summary>
+		public float LengthScale
+		{
+			get
+			{
+				return scale_;
+			}
+			set
+			{
+				scale_ = value;
+			}
+		}
+		private float scale_ = 1.0f;
+
+	}
+}
diff --git a/nplot/nplot/IDrawable.cs b/nplot/nplot/IDrawable.cs
new file mode 100644
index 0000000..5a5859d
--- /dev/null
+++ b/nplot/nplot/IDrawable.cs
@@ -0,0 +1,72 @@
+/*
+NPlot - A charting library for .NET
+
+IDrawable.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// Defines a Draw method for drawing objects against an x and y
+	/// Physical Axis.
+	/// </summary>
+	public interface IDrawable
+	{
+		/// <summary>
+		/// Draws this object against an x and y PhysicalAxis.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="xAxis">The physical x-axis to draw against.</param>
+		/// <param name="yAxis">The physical y-axis to draw against.</param>
+		void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis );
+	}
+}
diff --git a/nplot/nplot/IDrawable3D.cs b/nplot/nplot/IDrawable3D.cs
new file mode 100644
index 0000000..b04718c
--- /dev/null
+++ b/nplot/nplot/IDrawable3D.cs
@@ -0,0 +1,27 @@
+// ******** experimental ******** 
+/*
+using System;
+using System.Drawing;
+
+namespace NPlot
+{
+	/// <summary>
+	/// Defines a Draw method for drawing objects against x, y and z
+	/// Physical Axes.
+	/// </summary>
+	public interface IDrawable3D
+	{
+		/// <summary>
+		/// Draws this object against x, y and z PhysicalAxes.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="xAxis">The physical x-axis to draw against.</param>
+		/// <param name="yAxis">The physical y-axis to draw against.</param>
+		/// <param name="zAxis">The physical z-axis to draw against.</param>
+		/// <remarks>Other or different parameters will probably be needed here - basically 
+		/// the supplied parameters need to allow the Draw method to do the world -> physical
+		/// transform.</remarks>
+		void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis, PhysicalAxis zAxis );
+	}
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/IGradient.cs b/nplot/nplot/IGradient.cs
new file mode 100644
index 0000000..a3a62c0
--- /dev/null
+++ b/nplot/nplot/IGradient.cs
@@ -0,0 +1,73 @@
+/*
+NPlot - A charting library for .NET
+
+IGradient.cs
+Copyright (C) 2003-2004
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Defines a gradient.
+	/// </summary>
+	public interface IGradient
+	{
+
+		/// <summary>
+		/// Gets a color corresponding to a number between 0.0 and 1.0 inclusive.
+		/// </summary>
+		/// <param name="prop">the number to get corresponding color for (between 0.0 and 1.0)</param>
+		/// <returns>The color corresponding to the supplied number.</returns>
+		Color GetColor( double prop );
+
+	}
+}
diff --git a/nplot/nplot/IMeshPlot.cs b/nplot/nplot/IMeshPlot.cs
new file mode 100644
index 0000000..5f3f381
--- /dev/null
+++ b/nplot/nplot/IMeshPlot.cs
@@ -0,0 +1,49 @@
+/*
+NPlot - A charting library for .NET
+
+IMeshPlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+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 following text in 
+   the documentation and / or other materials provided with the 
+   distribution: 
+   
+   "This product includes software developed as part of 
+   the NPlot charting library project available from: 
+   http://www.nplot.com/"; 
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+$Id: IMeshPlot.cs,v 1.9 2004/10/23 07:08:35 mhowlett Exp $
+
+*/
+
+namespace NPlot
+{
+	/// <summary>
+	/// TODO
+	/// </summary>
+	public interface IMeshPlot : IPlot
+	{
+
+	}
+}
diff --git a/nplot/nplot/IPlot.cs b/nplot/nplot/IPlot.cs
new file mode 100644
index 0000000..60e3c16
--- /dev/null
+++ b/nplot/nplot/IPlot.cs
@@ -0,0 +1,104 @@
+/*
+NPlot - A charting library for .NET
+
+IPlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Drawing;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Defines the interface for objects that (a) can draw a representation of 
+	/// themselves in the legend and (b) can recommend a good axis to draw themselves
+	/// against.
+	/// </summary>
+	public interface IPlot : IDrawable
+	{	
+	
+		/// <summary>
+		/// Method used to draw a representation of the plot in a legend.
+		/// </summary>
+		void DrawInLegend( Graphics g, Rectangle startEnd );
+
+		
+		/// <summary>
+		/// The label associated with the plot [used in legend]
+		/// </summary>
+		string Label { get; set; }
+
+		
+		/// <summary>
+		/// Whether or not to include an entry for this plot in the legend if it exists.
+		/// </summary>
+		bool ShowInLegend { get; set; }
+
+
+		/// <summary>
+		/// The method used to set the default abscissa axis.
+		/// </summary>
+		Axis SuggestXAxis();
+
+		
+		/// <summary>
+		/// The method used to set the default ordinate axis.
+		/// </summary>
+		Axis SuggestYAxis();
+
+
+		/// <summary>
+		/// Write data associated with the plot as text.
+		/// </summary>
+		/// <param name="sb">the string builder to write to.</param>
+		/// <param name="region">Only write out data in this region if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, only data in region is written, else all data is written.</param>
+		void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion );
+
+	}
+}
\ No newline at end of file
diff --git a/nplot/nplot/IPlot3D.cs b/nplot/nplot/IPlot3D.cs
new file mode 100644
index 0000000..eb1d441
--- /dev/null
+++ b/nplot/nplot/IPlot3D.cs
@@ -0,0 +1,33 @@
+/*
+using System;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// 3D plottable objects implement this interface.
+	/// </summary>
+	public interface IPlot3D
+	{
+
+		/// <summary>
+		/// The method used to set the default x axis.
+		/// </summary>
+		Axis SuggestXAxis();
+
+		/// <summary>
+		/// The method used to set the default y axis.
+		/// </summary>
+		Axis SuggestYAxis();
+
+		/// <summary>
+		/// The method used to set the default z axis.
+		/// </summary>
+		Axis SuggestZAxis();
+
+	}
+
+
+}
+
+*/
\ No newline at end of file
diff --git a/nplot/nplot/IPlotSurface2D.cs b/nplot/nplot/IPlotSurface2D.cs
new file mode 100644
index 0000000..0f74a49
--- /dev/null
+++ b/nplot/nplot/IPlotSurface2D.cs
@@ -0,0 +1,255 @@
+/*
+NPlot - A charting library for .NET
+
+IPlotSurface2D.cs
+Copyright (C) 2003
+Paolo Pierini, Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Defines the PlotSurface2D interface - All specific PlotSurface2D classes
+	/// that use PlotSurface2D for their underlying operations should implement
+	/// this class.
+	/// </summary>
+	public interface IPlotSurface2D
+	{
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+		/// the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">The IDrawable object to add to the plot surface.</param>
+		/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+		void Add( IDrawable p, int zOrder );
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface against the specified axes. If
+		/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">the IDrawable object to add to the plot surface</param>
+		/// <param name="xp">the x-axis to add the plot against.</param>
+		/// <param name="yp">the y-axis to add the plot against.</param>
+		/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+		void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp, NPlot.PlotSurface2D.YAxisPosition yp, int zOrder );
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+		/// the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">The IDrawable object to add to the plot surface.</param>
+		void Add(IDrawable p);
+
+		
+		/// <summary>
+		/// Adds a drawable object to the plot surface against the specified axes. If
+		/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">the IDrawable object to add to the plot surface</param>
+		/// <param name="xax">the x-axis to add the plot against.</param>
+		/// <param name="yax">the y-axis to add the plot against.</param>
+		void Add(IDrawable p, NPlot.PlotSurface2D.XAxisPosition xax, NPlot.PlotSurface2D.YAxisPosition yax);
+		
+		
+		/// <summary>
+		/// Clears the PlotSurface2D.
+		/// </summary>
+		void Clear();
+
+
+		/// <summary>
+		/// Gets or Sets the legend to use with this plot surface.
+		/// </summary>
+		NPlot.Legend Legend { get; set; }
+
+		/// <summary>
+		/// Setting this value determines the order (relative to IDrawables added to the plot surface)
+		/// that the legend is drawn.
+		/// </summary>
+		int LegendZOrder { get; set; }
+
+		/// <summary>
+		/// The distance in pixels to leave between of the edge of the bounding rectangle
+		/// supplied to the Draw method, and the markings that make up the plot.
+		/// </summary>
+		int Padding { get; set; }
+		
+
+		/// <summary>
+		/// A color used to paint the plot background. Mutually exclusive with PlotBackImage and PlotBackBrush
+		/// </summary>
+		System.Drawing.Color PlotBackColor { set; }
+		
+
+		/// <summary>
+		/// An imaged used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+		/// </summary>
+		System.Drawing.Bitmap PlotBackImage { set; }
+
+
+		/// <summary>
+		/// A Rectangle brush used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+		/// </summary>
+		IRectangleBrush PlotBackBrush { set; }
+
+
+		/// <summary>
+		/// The plot surface title.
+		/// </summary>
+		string Title { get; set; }
+		
+
+		/// <summary>
+		/// Whether or not the title will be scaled according to size of the plot 
+		/// surface.
+		/// </summary>
+		bool AutoScaleTitle { get; set; }
+
+
+		/// <summary>
+		/// When plots are added to the plot surface, the axes they are attached to
+		/// are immediately modified to reflect data of the plot. If 
+		/// AutoScaleAutoGeneratedAxes is true when a plot is added, the axes will
+		/// be turned in to auto scaling ones if they are not already [tick marks,
+		/// tick text and label size scaled to size of plot surface]. If false,
+		/// axes will not be autoscaling.
+		/// </summary>
+		bool AutoScaleAutoGeneratedAxes { get; set; }
+
+
+		/// <summary>
+		/// Sets the title to be drawn using a solid brush of this color.
+		/// </summary>
+		System.Drawing.Color TitleColor { set; }
+
+
+		/// <summary>
+		/// The brush used for drawing the title.
+		/// </summary>
+		System.Drawing.Brush TitleBrush { get; set; }
+
+
+		/// <summary>
+		/// The plot title font.
+		/// </summary>
+		System.Drawing.Font TitleFont { get; set; }
+
+
+		/// <summary>
+		/// Smoothing mode to use when drawing plots.
+		/// </summary>
+		System.Drawing.Drawing2D.SmoothingMode SmoothingMode { get; set; }
+
+
+		/// <summary>
+		/// Add an axis constraint to the plot surface. Axis constraints can
+		/// specify relative world-pixel scalings, absolute axis positions etc.
+		/// </summary>
+		/// <param name="c">The axis constraint to add.</param>
+		void AddAxesConstraint( AxesConstraint c );
+
+
+		/// <summary>
+		/// The bottom abscissa axis.
+		/// </summary>
+		Axis XAxis1 { get; set; }
+
+
+		/// <summary>
+		/// The top abscissa axis.
+		/// </summary>
+		Axis XAxis2 { get; set; }
+
+
+		/// <summary>
+		/// The left ordinate axis.
+		/// </summary>
+		Axis YAxis1 { get; set; }
+
+
+		/// <summary>
+		/// The right ordinate axis.
+		/// </summary>
+		Axis YAxis2 { get; set; }
+
+
+		/// <summary>
+		/// Remove a drawable object from the plot surface.
+		/// </summary>
+		/// <param name="p">the object to remove</param>
+		/// <param name="updateAxes">whether or not to update the axes after removal.</param>
+		void Remove( IDrawable p, bool updateAxes );
+
+
+		/// <summary>
+		/// Gets an array list containing all drawables currently added to the PlotSurface2D.
+		/// </summary>
+		ArrayList Drawables { get; }
+
+/*
+		/// <summary>
+		/// Calculates axes approprate to IPlots on PlotSurface. Note that 
+		/// this is done automatically as a new plot is added. You may wish
+		/// to call this again if you update data in the plot. 
+		/// </summary>
+		void AutoCalculateAxes();
+
+
+		/// <summary>
+		/// C
+		/// </summary>
+		/// <param name="p"></param>
+		void UpdateAxes( IPlot p );
+*/
+
+	}
+}
diff --git a/nplot/nplot/IPlotSurface2Dnew.cs b/nplot/nplot/IPlotSurface2Dnew.cs
new file mode 100644
index 0000000..526f947
--- /dev/null
+++ b/nplot/nplot/IPlotSurface2Dnew.cs
@@ -0,0 +1,19 @@
+// ******** experimental ******** 
+/*
+namespace NPlot
+{
+	/// <summary>
+	/// Defines the PlotSurface2Dnew interface - All specific PlotSurface2Dnew classes
+	/// that use PlotSurface2Dnew for their underlying operations should implement
+	/// this class. TODO: this is experimental.
+	/// </summary>
+	public interface IPlotSurface2Dnew
+	{
+		/// <summary>
+		/// The distance in pixels to leave between of the edge of the bounding rectangle
+		/// supplied to the Draw method, and the markings that make up the plot.
+		/// </summary>
+		int Padding { get; set; }
+	}
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/IPlotSurface3D.cs b/nplot/nplot/IPlotSurface3D.cs
new file mode 100644
index 0000000..3f850fd
--- /dev/null
+++ b/nplot/nplot/IPlotSurface3D.cs
@@ -0,0 +1,17 @@
+// ******** experimental ******** 
+/*
+using System;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// TODO.
+	/// </summary>
+	public interface IPlotSurface3D
+	{
+
+	}
+
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/ISequencePlot.cs b/nplot/nplot/ISequencePlot.cs
new file mode 100644
index 0000000..d19c71b
--- /dev/null
+++ b/nplot/nplot/ISequencePlot.cs
@@ -0,0 +1,85 @@
+/*
+NPlot - A charting library for .NET
+
+IPlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Defines an mix-in style interface for plots that use SequenceAdapter to interpret supplied data.
+	/// </summary>
+	public interface ISequencePlot
+	{
+
+		/// <summary>
+		/// Gets or sets the source containing a list of values used to populate the plot object.
+		/// </summary>
+		object DataSource { get; set; }
+
+		/// <summary>
+		/// Gets or sets the specific data member in a multimember data source to get data from.
+		/// </summary>
+		string DataMember { get; set; }
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the abscissa [x] axis.
+		/// </summary>
+		object AbscissaData { get; set; }
+
+		/// <summary>
+		/// Gets or sets the data, or column name for the ordinate [y] axis.
+		/// </summary>
+		object OrdinateData { get; set; }
+
+	}
+
+}
diff --git a/nplot/nplot/ISurface.cs b/nplot/nplot/ISurface.cs
new file mode 100644
index 0000000..612d18a
--- /dev/null
+++ b/nplot/nplot/ISurface.cs
@@ -0,0 +1,98 @@
+/*
+NPlot - A charting library for .NET
+
+ISurface.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Windows.Forms;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// All PlotSurface's implement this interface.
+	/// </summary>
+	/// <remarks>Some of the parameter lists will change to be made more uniform.</remarks>
+	public interface ISurface
+	{
+
+		/// <summary>
+		/// Provides functionality for drawing the control.
+		/// </summary>
+		/// <param name="pe">paint event args</param>
+		/// <param name="width">width of the control.</param>
+		/// <param name="height">height of the control.</param>
+		void DoPaint( PaintEventArgs pe, int width, int height );
+		
+	
+		/// <summary>
+		/// Provides functionality for handling mouse up events.
+		/// </summary>
+		/// <param name="e">mouse event args</param>
+		/// <param name="ctr">the control</param>
+		void DoMouseUp( MouseEventArgs e, System.Windows.Forms.Control ctr );
+		
+		
+		/// <summary>
+		/// Provides functionality for handling mouse move events.
+		/// </summary>
+		/// <param name="e">mouse event args</param>
+		/// <param name="ctr">the control</param>
+		void DoMouseMove( MouseEventArgs e, System.Windows.Forms.Control ctr );
+		
+		
+		/// <summary>
+		/// Provides functionality for handling mouse down events.
+		/// </summary>
+		/// <param name="e">mouse event args</param>
+		void DoMouseDown( MouseEventArgs e );
+	
+	}
+
+}
diff --git a/nplot/nplot/ITransform2D.cs b/nplot/nplot/ITransform2D.cs
new file mode 100644
index 0000000..581c0a2
--- /dev/null
+++ b/nplot/nplot/ITransform2D.cs
@@ -0,0 +1,78 @@
+/*
+NPlot - A charting library for .NET
+
+ITransform2D.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// This interface is useful in the Plot classes for transforming 
+	/// world to physical coordinates. Create on using the GetTransformer
+	/// static method in Transform2D.
+	/// </summary>
+	public interface ITransform2D
+	{
+
+		/// <summary>
+		/// Transforms the given world point to physical coordinates
+		/// </summary>
+		PointF Transform( double x, double y );
+
+		/// <summary>
+		/// Transforms the given world point to physical coordinates
+		/// </summary>
+		PointF Transform( PointD worldPoint );
+
+	}
+
+}
diff --git a/nplot/nplot/ImagePlot.cs b/nplot/nplot/ImagePlot.cs
new file mode 100644
index 0000000..caf70f7
--- /dev/null
+++ b/nplot/nplot/ImagePlot.cs
@@ -0,0 +1,369 @@
+/*
+NPlot - A charting library for .NET
+
+ImagePlot.cs
+Copyright (C) 2003-2004
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Encapsulates functionality for plotting data as a 2D image chart.
+	/// </summary>
+	public class ImagePlot : IPlot
+	{
+		private double[,] data_;
+		private double xStart_ = 0.0;
+		private double xStep_ = 1.0;
+		private double yStart_ = 0.0;
+		private double yStep_ = 1.0;
+		
+
+		/// <summary>
+		/// At or below which value a minimum gradient color should be used.
+		/// </summary>
+		public double DataMin      
+		{
+			get          
+			{
+				return dataMin_;
+			}
+			set            
+			{
+				dataMin_ = value;
+			}
+		}
+		private double dataMin_;
+
+
+		/// <summary>
+		/// At or above which value a maximum gradient color should be used.
+		/// </summary>
+		public double DataMax
+		{
+			get
+			{
+				return dataMax_;
+			}
+			set
+			{
+				dataMax_ = value;
+			}
+		}
+		private double dataMax_;
+
+
+		/// <summary>
+		/// Calculates the minimum and maximum values of the data array.
+		/// </summary>
+		private void calculateMinMax()
+		{
+			dataMin_ = data_[0,0];
+			dataMax_ = data_[0,0];
+			for (int i=0; i<data_.GetLength(0); ++i)
+			{
+				for (int j=0; j<data_.GetLength(1); ++j)
+				{
+					if (data_[i,j]<dataMin_) 
+					{
+						dataMin_ = data_[i,j];
+					}
+					if (data_[i,j]>dataMax_) 
+					{
+						dataMax_ = data_[i,j];
+					}
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="data">the 2D array to plot</param>
+		/// <param name="xStart">the world value corresponding to the 1st position in the x-direction</param>
+		/// <param name="xStep">the world step size between pixels in the x-direction.</param>
+		/// <param name="yStart">the world value corresponding to the 1st position in the y-direction</param>
+		/// <param name="yStep">the world step size between pixels in the y-direction.</param>
+		/// <remarks>no adapters for this yet - when we get some more 2d
+		/// plotting functionality, then perhaps create some.</remarks>
+		public ImagePlot( double[,] data, double xStart, double xStep, double yStart, double yStep )
+		{
+
+#if CHECK_ERRORS
+			if (data == null || data.GetLength(0) == 0 || data.GetLength(1) == 0)
+			{
+				throw new NPlotException( "ERROR: ImagePlot.ImagePlot: Data null, or zero length" );
+			}
+#endif
+
+			this.data_ = data;
+			this.xStart_ = xStart;
+			this.xStep_ = xStep;
+			this.yStart_ = yStart;
+			this.yStep_ = yStep;
+			this.calculateMinMax();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="data">The 2D array to plot.</param>
+		public ImagePlot( double[,] data )
+		{
+			this.data_ = data;
+			this.calculateMinMax();
+		}
+
+
+		/// <summary>
+		/// Draw on to the supplied graphics surface against the supplied axes.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		/// <remarks>TODO: block positions may be off by a pixel or so. maybe. Re-think calculations</remarks>
+		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			if ( data_==null || data_.GetLength(0) == 0 || data_.GetLength(1) == 0 )
+			{
+				return;
+			}
+
+			double worldWidth = xAxis.Axis.WorldMax - xAxis.Axis.WorldMin;
+			double numBlocksHorizontal = worldWidth / this.xStep_;
+			double worldHeight = yAxis.Axis.WorldMax - yAxis.Axis.WorldMin;
+			double numBlocksVertical = worldHeight / this.yStep_;
+
+			double physicalWidth = xAxis.PhysicalMax.X - xAxis.PhysicalMin.X;
+			double blockWidth = physicalWidth / numBlocksHorizontal;
+			bool wPositive = true;
+			if (blockWidth < 0.0)
+			{
+				wPositive = false;
+			}
+			blockWidth = Math.Abs(blockWidth)+1;
+
+			double physicalHeight = yAxis.PhysicalMax.Y - yAxis.PhysicalMin.Y;
+			double blockHeight = physicalHeight / numBlocksVertical;
+			bool hPositive = true;
+			if (blockHeight < 0.0)
+			{
+				hPositive = false;
+			}
+			blockHeight = Math.Abs(blockHeight)+1;
+
+			for (int i=0; i<data_.GetLength(0); ++i)
+			{
+				for (int j=0; j<data_.GetLength(1); ++j)
+				{
+					double wX = (double)j*this.xStep_ + xStart_;
+					double wY = (double)i*this.yStep_ + yStart_;
+					if ( !hPositive )
+					{
+						wY += yStep_;
+					}
+					if (!wPositive )
+					{
+						wX += xStep_;
+					}
+
+					if (this.center_)
+					{
+						wX -= this.xStep_/2.0;
+						wY -= this.yStep_/2.0;
+					}
+					Pen p = new Pen( this.Gradient.GetColor( (data_[i,j]-this.dataMin_)/(this.dataMax_-this.dataMin_) ) );
+					int x = (int)xAxis.WorldToPhysical(wX,false).X;
+					int y = (int)yAxis.WorldToPhysical(wY,false).Y;
+					g.FillRectangle( p.Brush,
+						x,
+						y, 
+						(int)blockWidth,
+						(int)blockHeight);
+					//g.DrawRectangle(Pens.White,x,y,(int)blockWidth,(int)blockHeight);
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// The gradient that specifies the mapping between value and color.
+		/// </summary>
+		/// <remarks>memory allocation in get may be inefficient.</remarks>
+		public IGradient Gradient
+		{
+			get
+			{
+				if (gradient_ == null)
+				{
+					// TODO: suboptimal.
+					gradient_ = new LinearGradient( Color.FromArgb(255,255,255), Color.FromArgb(0,0,0) );
+				}
+				return this.gradient_;
+			}
+			set
+			{
+				this.gradient_ = value;
+			}
+		}
+		private IGradient gradient_;
+
+
+		/// <summary>
+		/// Draws a representation of this plot in the legend.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public void DrawInLegend( Graphics g, Rectangle startEnd )
+		{
+			// not implemented yet.
+		}
+
+
+		/// <summary>
+		/// A label to associate with the plot - used in the legend.
+		/// </summary>
+		public string Label
+		{
+			get
+			{
+				return label_;
+			}
+			set
+			{
+				this.label_ = value;
+			}
+		}
+		private string label_ = "";
+
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			if (this.center_)
+			{
+				return new LinearAxis( this.xStart_ - this.xStep_/2.0, this.xStart_ + this.xStep_ * data_.GetLength(1) - this.xStep_/2.0 );
+			}
+			
+			return new LinearAxis( this.xStart_, this.xStart_ + this.xStep_ * data_.GetLength(1) );
+		}
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+			if (this.center_)
+			{
+				return new LinearAxis( this.yStart_ - this.yStep_/2.0, this.yStart_ + this.yStep_ * data_.GetLength(0) - this.yStep_/2.0 );
+			}
+			
+			return new LinearAxis( this.yStart_, this.yStart_ + this.yStep_ * data_.GetLength(0) );
+		}
+
+
+		/// <summary>
+		/// If true, pixels are centered on their respective coordinates. If false, they are drawn
+		/// between their coordinates and the coordinates of the the next point in each direction.
+		/// </summary>
+		public bool Center
+		{
+			set
+			{
+				center_ = value;
+			}
+			get
+			{
+				return center_;
+			}
+		}
+		private bool center_ = true;
+
+
+		/// <summary>
+		/// Whether or not to include an entry for this plot in the legend if it exists.
+		/// </summary>
+		public bool ShowInLegend
+		{
+			get
+			{
+				return showInLegend_;
+			}
+			set
+			{
+				this.showInLegend_ = value;
+			}
+		}
+		private bool showInLegend_ = true;
+
+
+		/// <summary>
+		/// Write data associated with the plot as text.
+		/// </summary>
+		/// <param name="sb">the string builder to write to.</param>
+		/// <param name="region">Only write out data in this region if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, only data in region is written, else all data is written.</param>
+		/// <remarks>TODO: not implemented.</remarks>
+		public void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion )
+		{
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/LabelAxis.cs b/nplot/nplot/LabelAxis.cs
new file mode 100644
index 0000000..75d0c4e
--- /dev/null
+++ b/nplot/nplot/LabelAxis.cs
@@ -0,0 +1,356 @@
+/*
+NPlot - A charting library for .NET
+
+LabelAxis.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
+using System.Drawing;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Allows the creation of axes with any number of user defined labels at
+	/// user defined world values along the axis. 
+	/// </summary>
+	public class LabelAxis : Axis
+	{
+
+		/// <summary>
+		/// Deep copy of LabelAxis.
+		/// </summary>
+		/// <returns>A copy of the LinearAxis Class.</returns>
+		public override object Clone()
+		{
+			LabelAxis a = new LabelAxis();
+			// ensure that this isn't being called on a derived type. If it is, then oh no!
+			if (this.GetType() != a.GetType())
+			{
+				throw new NPlotException( "Error. Clone method is not defined in derived type." );
+			}
+			DoClone( this, a );
+			return a;
+		}
+
+		/// <summary>
+		/// Helper method for Clone.
+		/// </summary>
+		/// <param name="a">The original object to clone.</param>
+		/// <param name="b">The cloned object.</param>
+		protected static void DoClone( LabelAxis b, LabelAxis a )
+		{
+			Axis.DoClone( b, a );
+
+			a.labels_ = (ArrayList)b.labels_.Clone();
+			a.numbers_ = (ArrayList)b.numbers_.Clone();
+
+			a.ticksBetweenText_ = b.ticksBetweenText_;
+			a.sortDataIfNecessary_ = b.sortDataIfNecessary_;
+		}
+
+
+		/// <summary>
+		/// Initialise LabelAxis to default state.
+		/// </summary>
+		private void Init()
+		{
+			labels_ = new ArrayList();
+			numbers_ = new ArrayList();
+		}
+
+
+		/// <summary>
+		/// Copy constructor
+		/// </summary>
+		/// <param name="a">The Axis to clone.</param>
+		/// <remarks>TODO: [review notes] I don't think this will work as desired.</remarks>
+		public LabelAxis( Axis a )
+			: base( a )
+		{
+			Init();
+		}
+
+		/// <summary>
+		/// Default constructor
+		/// </summary>
+		public LabelAxis()
+			: base()
+		{
+			Init();
+		}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="worldMin">Minimum world value</param>
+		/// <param name="worldMax">Maximum world value</param>
+		public LabelAxis( double worldMin, double worldMax )
+			: base( worldMin, worldMax )
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Adds a label to the axis
+		/// </summary>
+		/// <param name="name">The label</param>
+		/// <param name="val">The world value at which to place the label</param>
+		public void AddLabel( string name, double val )
+		{
+			labels_.Add( name );
+			numbers_.Add( val );
+		}
+		
+
+		/// <summary>
+		/// Given Graphics surface, and physical extents of axis, draw ticks and
+		/// associated labels.
+		/// </summary>
+		/// <param name="g">The GDI+ Graphics surface on which to draw.</param>
+		/// <param name="physicalMin">The physical location of the world min point</param>
+		/// <param name="physicalMax">The physical location of the world max point</param>
+		/// <param name="boundingBox">out: smallest box that completely encompasses all of the ticks and tick labels.</param>
+		/// <param name="labelOffset">out: a suitable offset from the axis to draw the axis label.</param>
+		protected override void DrawTicks( 
+			Graphics g, 
+			Point physicalMin, 
+			Point physicalMax, 
+			out object labelOffset,
+			out object boundingBox )
+		{
+
+			Point tLabelOffset;
+			Rectangle tBoundingBox;
+
+			labelOffset = this.getDefaultLabelOffset( physicalMin, physicalMax );
+			boundingBox = null;
+
+			// draw the tick labels (but not the ticks).
+			PointF lastPos = WorldToPhysical( (double)numbers_[0], physicalMin, physicalMax, true );
+			for (int i=0; i<labels_.Count; ++i)
+			{
+				
+				if ((double)numbers_[i] > WorldMin && (double)numbers_[i] < WorldMax)
+				{
+					
+					// check to make sure labels are far enough appart.
+					PointF thisPos = WorldToPhysical( (double)numbers_[i], physicalMin, physicalMax, true );
+					float dist = Utils.Distance( thisPos, lastPos );
+
+					if ( i==0 || (dist > this.PhysicalSpacingMin) )
+					{
+						lastPos = thisPos;
+						
+						this.DrawTick( g, (double)numbers_[i], 0, 
+							(string)labels_[i],
+							new Point(0,0), 
+							physicalMin, physicalMax,
+							out tLabelOffset, out tBoundingBox );
+					
+						Axis.UpdateOffsetAndBounds( 
+							ref labelOffset, ref boundingBox, 
+							tLabelOffset, tBoundingBox );
+					}
+				}
+			}
+
+			// now draw the ticks (which might not be aligned with the tick text).
+			ArrayList largeTickPositions;
+			ArrayList smallTickPositions;
+			WorldTickPositions_FirstPass( physicalMin, physicalMax, out largeTickPositions, out smallTickPositions );
+			lastPos = WorldToPhysical( (double)largeTickPositions[0], physicalMin, physicalMax, true );
+			for (int i=0; i<largeTickPositions.Count; ++i)
+			{
+				double tickPos = (double)largeTickPositions[i];
+
+				// check to see that labels are far enough appart. 
+				PointF thisPos = WorldToPhysical( tickPos, physicalMin, physicalMax, true );
+				float dist = Utils.Distance( thisPos, lastPos );
+				if ( (i==0) || (dist> this.PhysicalSpacingMin) )
+				{
+					lastPos = thisPos;
+
+					this.DrawTick( g, tickPos, LargeTickSize, 
+						"",
+						new Point(0,0), 
+						physicalMin, physicalMax,
+						out tLabelOffset, out tBoundingBox );
+					
+					Axis.UpdateOffsetAndBounds( 
+						ref labelOffset, ref boundingBox, 
+						tLabelOffset, tBoundingBox );
+				}
+			}
+
+		}
+
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the large ticks. 
+		/// 
+		/// Label axes do not have small ticks.
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">null</param>
+		internal override void WorldTickPositions_FirstPass(
+			Point physicalMin, 
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+			smallTickPositions = null;
+			largeTickPositions = new ArrayList();
+
+			// if ticks correspond to position of text
+			if (!ticksBetweenText_)
+			{
+				for (int i=0; i<labels_.Count; ++i)
+				{
+					if ((double)numbers_[i] > WorldMin && (double)numbers_[i] < WorldMax)
+					{
+						largeTickPositions.Add( numbers_[i] );
+					}
+				}
+			}
+
+			// if ticks correspond to gaps between text
+			else
+			{
+				ArrayList numbers_copy;
+				if (sortDataIfNecessary_)
+				{
+					numbers_copy = (ArrayList)numbers_.Clone(); // shallow copy.
+					numbers_copy.Sort();
+				}
+				else
+				{
+					numbers_copy = numbers_;
+				}
+
+				for (int i=1; i<labels_.Count; ++i)
+				{
+					double worldPosition = ((double)numbers_copy[i] + (double)numbers_copy[i-1])/2.0;
+					if (worldPosition > WorldMin && worldPosition < WorldMax)
+					{
+						largeTickPositions.Add( worldPosition );
+					}
+				}
+			}
+
+		}
+
+
+		/// <summary>
+		/// If true, large ticks are drawn between the labels, rather
+		/// than at the position of the labels.
+		/// </summary>
+		public bool TicksBetweenText
+		{
+			get
+			{
+				return ticksBetweenText_;
+			}
+			set
+			{
+				ticksBetweenText_ = value;
+			}
+		}
+		private bool ticksBetweenText_ = false;
+
+
+		/// <summary>
+		/// If your data may be be specified out of order (that is 
+		/// abscissa values with a higher index may be less than
+		/// abscissa values of a lower index), then data sorting 
+		/// may be necessary to implement some of the functionality
+		/// of this object. If you know your data is already 
+		/// ordered with abscissa values lowest -> highest, then
+		/// you may set this to false. It's default is true.
+		/// </summary>
+		public bool SortDataIfNecessary
+		{
+			get
+			{
+				return sortDataIfNecessary_;
+			}
+			set
+			{
+				sortDataIfNecessary_ = value;
+			}
+		}
+		private bool sortDataIfNecessary_ = true;
+
+
+		/// <summary>
+		/// If consecutive labels are less than this number of pixels appart, 
+		/// some of the labels will not be drawn.
+		/// </summary>
+		public int PhysicalSpacingMin
+		{
+			get
+			{
+				return physicalSpacingMin_;
+			}
+			set
+			{
+				physicalSpacingMin_ = value;
+			}
+		}
+		private int physicalSpacingMin_ = 0;
+
+
+		private ArrayList labels_;
+		private ArrayList numbers_;
+	}
+}
diff --git a/nplot/nplot/LabelPointPlot.cs b/nplot/nplot/LabelPointPlot.cs
new file mode 100644
index 0000000..28e1f8d
--- /dev/null
+++ b/nplot/nplot/LabelPointPlot.cs
@@ -0,0 +1,324 @@
+/*
+NPlot - A charting library for .NET
+
+LabelPointPlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Data;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Encapsulates functionality
+	/// </summary>
+	public class LabelPointPlot : PointPlot, ISequencePlot
+	{
+
+		/// <summary>
+		/// This class us used in conjunction with SequenceAdapter to interpret data
+		/// specified to the TextPlot class.
+		/// </summary>
+		private class TextDataAdapter
+		{
+
+			private object data_;
+			private object dataSource_;
+			private string dataMember_;
+
+			public TextDataAdapter( object dataSource, string dataMember, object data )
+			{
+				this.data_ = data;
+				this.dataSource_ = dataSource;
+				this.dataMember_ = dataMember;
+			}
+
+			public string this[int i]
+			{
+				get
+				{
+
+					// this is inefficient [could set up delegates in constructor].
+
+					if (data_ is string[])
+					{
+						return ((string[])data_)[i];
+					}
+					
+					if (data_ is string)
+					{
+						if (dataSource_ == null)
+						{
+							throw new NPlotException( "Error: DataSource null" );
+						}
+
+						System.Data.DataRowCollection rows;
+
+						if ( dataSource_ is System.Data.DataSet )
+						{
+							if (dataMember_ != null)
+							{
+								// TODO error check
+								rows = ((DataTable)((DataSet)dataSource_).Tables[dataMember_]).Rows;
+							}
+							else
+							{
+								// TODO error check
+								rows = ((DataTable)((DataSet)dataSource_).Tables[0]).Rows;
+							}
+						}
+
+						else if (dataSource_ is System.Data.DataTable )
+						{
+							rows = ((DataTable)dataSource_).Rows;
+						}
+
+						else
+						{
+							throw new NPlotException ( "not implemented yet" );
+						}
+
+						return (string)((System.Data.DataRow)(rows[i]))[(string)data_];
+					}
+
+					if (data_ is System.Collections.ArrayList)
+					{
+						object dataPoint = ((System.Collections.ArrayList)data_)[i];
+						if (dataPoint is string)
+							return (string)dataPoint;
+						throw new NPlotException( "TextDataAdapter: data not in recognised format" );
+					}
+					
+					if (data_ == null)
+					{
+						return "text";
+					}
+
+					throw new NPlotException( "Text data not of recognised type" );
+				}
+			}
+
+
+			public int Count
+			{
+				get
+				{
+					// this is inefficient [could set up delegates in constructor].
+
+					if (data_ == null)
+					{
+						return 0;
+					}
+					if (data_ is string[])
+					{
+						return ((string[])data_).Length;
+					}
+					if (data_ is System.Collections.ArrayList)
+					{
+						return ((System.Collections.ArrayList)data_).Count;
+					}
+					throw new NPlotException( "Text data not in correct format" );
+				}
+			}
+
+		}
+
+
+		/// <summary>
+		/// Enumeration of all label positions relative to a point.
+		/// </summary>
+		public enum LabelPositions
+		{
+			/// <summary>
+			/// Above the point
+			/// </summary>
+			Above,
+			/// <summary>
+			/// Below the point
+			/// </summary>
+			Below,
+			/// <summary>
+			/// To the left of the point
+			/// </summary>
+			Left,
+			/// <summary>
+			/// To the right of the point
+			/// </summary>
+			Right
+		}
+
+
+		/// <summary>
+		/// Default Constructor
+		/// </summary>
+		public LabelPointPlot()
+		{
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="marker">The marker type to use for this plot.</param>
+		public LabelPointPlot( Marker marker ) 
+			: base( marker )
+		{
+		}
+
+		
+		/// <summary>
+		/// The position of the text label in relation to the point.
+		/// </summary>
+		public LabelPositions LabelTextPosition
+		{
+			get
+			{
+				return labelTextPosition_;
+			}
+			set
+			{
+				labelTextPosition_ = value;
+			}
+		}
+		private LabelPositions labelTextPosition_ = LabelPositions.Above;
+
+
+		/// <summary>
+		/// The text datasource to attach to each point.
+		/// </summary>
+		public object TextData
+		{
+			get
+			{
+				return textData_;
+			}
+			set
+			{
+				textData_ = value;
+			}
+		}
+		object textData_;
+
+
+		/// <summary>
+		/// The Font used to write text.
+		/// </summary>
+		public Font Font
+		{
+			get
+			{
+				return font_;
+			}
+			set
+			{
+				font_ = value;
+			}
+		}
+		private Font font_ = new Font( "Arial", 8.0f );
+
+
+		/// <summary>
+		/// Draws the plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public override void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			TextDataAdapter textData =
+				new TextDataAdapter( this.DataSource, this.DataMember, this.TextData );
+
+			// first plot the marker
+		    // we can do this cast, since the constructor accepts only this type!
+			for (int i=0; i<data.Count; ++i)
+			{
+				try
+				{
+					PointD pt = data[i];
+					if ( !Double.IsNaN(pt.X) && !Double.IsNaN(pt.Y) )
+					{
+						PointF xPos = xAxis.WorldToPhysical( pt.X, false);
+						PointF yPos = yAxis.WorldToPhysical( pt.Y, false);
+						Marker.Draw( g, (int)xPos.X, (int)yPos.Y );
+						if ( textData[i] != "" )
+						{
+							SizeF size = g.MeasureString( textData[i], this.Font );
+							switch (labelTextPosition_)
+							{
+								case LabelPositions.Above:
+									g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X-size.Width/2,yPos.Y-size.Height-Marker.Size*2/3));
+									break;
+								case LabelPositions.Below:
+									g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X-size.Width/2,yPos.Y+Marker.Size*2/3));
+									break;
+								case LabelPositions.Left:
+									g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X-size.Width-Marker.Size*2/3,yPos.Y-size.Height/2));
+									break;
+								case LabelPositions.Right:
+									g.DrawString( textData[i], font_, Brushes.Black, new PointF(xPos.X+Marker.Size*2/3,yPos.Y-size.Height/2));
+									break;
+							}
+						}
+					}
+				}
+				catch
+				{
+					throw new NPlotException("Error in TextPlot.Draw");
+				}
+			}
+
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/Legend.cs b/nplot/nplot/Legend.cs
new file mode 100644
index 0000000..00a133a
--- /dev/null
+++ b/nplot/nplot/Legend.cs
@@ -0,0 +1,352 @@
+/*
+NPlot - A charting library for .NET
+
+Legend.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+using System.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Legend functionality specific to Legends associated with a PlotSurface2D.
+	/// </summary>
+	public class Legend : LegendBase
+	{
+
+		/// <summary>
+		/// Enumeration of possible Legend placements.
+		/// </summary>
+		public enum Placement
+		{
+			/// <summary>
+			/// Inside the plot area.
+			/// </summary>
+			Inside = 0,
+			/// <summary>
+			/// Outside the plot area.
+			/// </summary>
+			Outside = 1
+		}
+
+		private int xOffset_;
+		private int yOffset_;
+		private PlotSurface2D.XAxisPosition xAttach_;
+		private PlotSurface2D.YAxisPosition yAttach_;
+		private Placement horizontalEdgePlacement_;
+		private Placement verticalEdgePlacement_;
+		private bool neverShiftAxes_;
+
+		/// <summary>
+		/// Whether or not the positions of the Axes may be shifted to make
+		/// room for the Legend. 
+		/// </summary>
+		public bool NeverShiftAxes
+		{
+			get
+			{
+				return neverShiftAxes_;
+			}
+			set
+			{
+				neverShiftAxes_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Offset from the chosen Y-Axis. TODO: better description.
+		/// </summary>
+		public int XOffset
+		{
+			get
+			{
+				return xOffset_;
+			}
+			set
+			{
+				xOffset_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Offset from the X-Axis. TODO: better description.
+		/// </summary>
+		public int YOffset
+		{
+			get
+			{
+				return yOffset_;
+			}
+			set
+			{
+				yOffset_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Whether or not to attach the legend on the inside of the top
+		/// or bottom axis (which, is specified using the AttachTo method) or the 
+		/// outside. 
+		/// </summary>
+		public Legend.Placement VerticalEdgePlacement
+		{
+			get
+			{
+				return verticalEdgePlacement_;
+			}
+			set
+			{
+				verticalEdgePlacement_ = value;
+			}
+		}
+
+		
+		/// <summary>
+		/// Whether or not to attach the legend on the inside of the
+		/// left or right axis (which, is specified using the AttachTo method) 
+		/// or the outside.
+		/// </summary>
+		public Legend.Placement HorizontalEdgePlacement
+		{
+			get
+			{
+				return horizontalEdgePlacement_;
+			}
+			set
+			{
+				horizontalEdgePlacement_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Specify the Axes to attach the legend to. 
+		/// </summary>
+		/// <param name="xa">Specify which horizontal axis the legend should be attached to.</param>
+		/// <param name="ya">Specify which vertical axis the legend should be attached to.</param>
+		public void AttachTo( PlotSurface2D.XAxisPosition xa, PlotSurface2D.YAxisPosition ya )
+		{
+			xAttach_ = xa;
+			yAttach_ = ya; 
+		}
+
+
+		/// <summary>
+		/// Default constructor.
+		/// </summary>
+		public Legend()
+		{
+			xAttach_ = PlotSurface2D.XAxisPosition.Top;
+			yAttach_ = PlotSurface2D.YAxisPosition.Right;
+			xOffset_ = 10;
+			yOffset_ = 1;
+			verticalEdgePlacement_ = Placement.Outside;
+			horizontalEdgePlacement_ = Placement.Inside;
+			neverShiftAxes_ = false;
+		}
+
+
+		/// <summary>
+		/// Updates the PlotSurface2D axes to compensate for the legend.
+		/// </summary>
+		/// <param name="pXAxis1">the bottom x axis</param>
+		/// <param name="pYAxis1">the left y axis</param>
+		/// <param name="pXAxis2">the top x axis</param>
+		/// <param name="pYAxis2">the right y axis</param>
+		/// <param name="plots">list of plots.</param>
+		/// <param name="scale">scale parameter (for text and other)</param>
+		/// <param name="padding">padding around plot within bounds.</param>
+		/// <param name="bounds">graphics surface bounds</param>
+		/// <param name="position">legend position</param>
+		public void UpdateAxesPositions( 
+			PhysicalAxis pXAxis1,
+			PhysicalAxis pYAxis1,
+			PhysicalAxis pXAxis2,
+			PhysicalAxis pYAxis2,
+			ArrayList plots,
+			float scale, int padding, Rectangle bounds,
+			out Point position )
+		{
+		
+			int leftIndent = 0;
+			int rightIndent = 0;
+			int bottomIndent = 0;
+			int topIndent = 0;
+
+			position = new Point(0,0);
+
+			// now determine if legend should change any of these (legend should be fully 
+			// visible at all times), and draw legend.
+
+			Rectangle legendWidthHeight = this.GetBoundingBox( new Point(0,0), plots, scale );
+
+			if (legendWidthHeight.Width > bounds.Width)
+			{
+				legendWidthHeight.Width = bounds.Width;
+			}
+
+			// (1) calculate legend position.
+
+			// y
+
+			position.Y = this.yOffset_;
+			
+			if ( this.xAttach_ == PlotSurface2D.XAxisPosition.Bottom )
+			{
+				position.Y += pYAxis1.PhysicalMin.Y;
+				if ( this.horizontalEdgePlacement_ == Legend.Placement.Inside )
+				{
+					position.Y -= legendWidthHeight.Height;
+				}
+			}
+			else
+			{
+				position.Y += pYAxis1.PhysicalMax.Y;
+				if ( this.horizontalEdgePlacement_ == Legend.Placement.Outside )
+				{
+					position.Y -= legendWidthHeight.Height;
+				}
+			}
+	
+			// x
+
+			position.X = this.xOffset_;
+		
+			if ( this.yAttach_ == PlotSurface2D.YAxisPosition.Left )
+			{
+				if ( this.verticalEdgePlacement_ == Legend.Placement.Outside ) 
+				{
+					position.X -= legendWidthHeight.Width;
+				}
+				position.X += pXAxis1.PhysicalMin.X;
+			}
+			else
+			{
+				if ( this.verticalEdgePlacement_ == Legend.Placement.Inside )
+				{
+					position.X -= legendWidthHeight.Width;
+				}
+				position.X += pXAxis1.PhysicalMax.X;
+			}
+
+
+			// determine update amounts for axes
+
+			if ( !this.neverShiftAxes_ )
+			{
+
+				if ( position.X < padding )
+				{
+					int changeAmount = -position.X + padding;
+					// only allow axes to move away from bounds.
+					if ( changeAmount > 0 )					
+					{
+						leftIndent = changeAmount;
+					}
+					position.X += changeAmount;
+				}
+
+				if ( position.X + legendWidthHeight.Width > bounds.Right - padding )
+				{
+					int changeAmount = (position.X - bounds.Right + legendWidthHeight.Width + padding );
+					// only allow axes to move away from bounds.
+					if ( changeAmount > 0.0f ) 
+					{
+						rightIndent = changeAmount;
+					}
+					position.X -= changeAmount;
+				}
+
+				if ( position.Y < padding )
+				{
+					int changeAmount = -position.Y + padding;
+					// only allow axes to move away from bounds.
+					if ( changeAmount > 0.0f )					
+					{
+						topIndent = changeAmount;
+					}
+					position.Y += changeAmount;
+				}
+
+				if ( position.Y + legendWidthHeight.Height > bounds.Bottom - padding )
+				{
+					int changeAmount = (position.Y - bounds.Bottom + legendWidthHeight.Height + padding );
+					// only allow axes to move away from bounds.
+					if ( changeAmount > 0.0f ) 
+					{
+						bottomIndent = changeAmount;
+					}
+					position.Y -= changeAmount;
+				}
+
+				// update axes.
+
+				pXAxis1.PhysicalMin = new Point( pXAxis1.PhysicalMin.X + leftIndent, pXAxis1.PhysicalMin.Y - bottomIndent );
+				pXAxis1.PhysicalMax = new Point( pXAxis1.PhysicalMax.X - rightIndent, pXAxis1.PhysicalMax.Y - bottomIndent );
+				pYAxis1.PhysicalMin = new Point( pYAxis1.PhysicalMin.X + leftIndent, pYAxis1.PhysicalMin.Y - bottomIndent );
+				pYAxis1.PhysicalMax = new Point( pYAxis1.PhysicalMax.X + leftIndent, pYAxis1.PhysicalMax.Y + topIndent );
+
+				pXAxis2.PhysicalMin = new Point( pXAxis2.PhysicalMin.X + leftIndent, pXAxis2.PhysicalMin.Y + topIndent );
+				pXAxis2.PhysicalMax = new Point( pXAxis2.PhysicalMax.X - rightIndent, pXAxis2.PhysicalMax.Y + topIndent );
+				pYAxis2.PhysicalMin = new Point( pYAxis2.PhysicalMin.X - rightIndent, pYAxis2.PhysicalMin.Y - bottomIndent );
+				pYAxis2.PhysicalMax = new Point( pYAxis2.PhysicalMax.X - rightIndent, pYAxis2.PhysicalMax.Y + topIndent );
+			
+			}
+
+		}
+
+	}
+
+}
diff --git a/nplot/nplot/LegendBase.cs b/nplot/nplot/LegendBase.cs
new file mode 100644
index 0000000..68e38e4
--- /dev/null
+++ b/nplot/nplot/LegendBase.cs
@@ -0,0 +1,459 @@
+/*
+NPlot - A charting library for .NET
+
+LegendBase.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
+using System.Drawing;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Provides functionality for drawing legends.
+	/// </summary>
+	/// <remarks>
+	/// The class is quite closely tied to PlotSurface2D. 
+	/// </remarks>
+	public class LegendBase
+	{
+
+		/// <summary>
+		/// Constructor.
+		/// </summary>
+		public LegendBase()
+		{
+			this.Font = new Font( new FontFamily("Arial"), 10, FontStyle.Regular, GraphicsUnit.Pixel );
+			this.BackgroundColor = Color.White;
+			this.BorderColor = Color.Black;
+			this.TextColor = Color.Black;
+			this.borderStyle_ = BorderType.Shadow;
+			this.autoScaleText_ = false;
+		}
+
+
+		/// <summary>
+		/// Get the bounding box of the rectangle.
+		/// </summary>
+		/// <param name="position">the position of the top left of the legend.</param>
+		/// <param name="plots">Array of plot objects to appear in the legend.</param>
+		/// <param name="scale">if the legend is set to scale, the amount to scale by.</param>>
+		/// <returns></returns>
+		/// <remarks>do implementation that doesn't call draw. Change xPos, yPos to PointF</remarks>
+		public Rectangle GetBoundingBox( Point position, ArrayList plots, float scale )
+		{
+			System.Drawing.Bitmap b = new System.Drawing.Bitmap(1,1);
+			Graphics g = Graphics.FromImage(b);
+			return this.Draw( g, position, plots, scale );
+		}
+
+
+		/// <summary>
+		/// Draw The legend
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw</param>
+		/// <param name="position">The position of the top left of the axis.</param>
+		/// <param name="plots">Array of plot objects to appear in the legend.</param>
+		/// <param name="scale">if the legend is set to scale, the amount to scale by.</param>
+		/// <returns>bounding box</returns>
+		public Rectangle Draw( Graphics g, Point position, ArrayList plots, float scale )
+		{
+
+			// first of all determine the Font to use in the legend.
+			Font textFont;
+			if (this.AutoScaleText)
+			{
+				textFont = Utils.ScaleFont( this.font_, scale );
+			}
+			else
+			{
+				textFont = this.font_;
+			}
+
+			// determine max width and max height of label strings and
+			// count the labels. 
+			int labelCount = 0;
+			int maxHt = 0;
+			int maxWd = 0;
+			int unnamedCount = 0;
+			for (int i=0; i<plots.Count; ++i)
+			{
+				if (!(plots[i] is IPlot))
+				{
+					continue;
+				}
+
+				IPlot p = (IPlot)plots[i];
+
+				if (!p.ShowInLegend)
+				{
+					continue;
+				}
+				string label = p.Label;
+				if (label == "")
+				{
+					unnamedCount += 1;
+					label = "Series " + unnamedCount.ToString();
+				}
+				SizeF labelSize = g.MeasureString( label, textFont );
+				if ( labelSize.Height > maxHt )
+				{
+					maxHt = (int)labelSize.Height;
+				}
+				if ( labelSize.Width > maxWd )
+				{
+					maxWd = (int)labelSize.Width;
+				}
+
+				++labelCount;
+			}
+
+			bool extendingHorizontally = numberItemsHorizontally_ == -1;
+			bool extendingVertically = numberItemsVertically_ == -1;
+
+
+			// determine width in legend items count units.
+			int widthInItemCount = 0;
+			if (extendingVertically)
+			{
+				if (labelCount >= numberItemsHorizontally_)
+				{
+					widthInItemCount = numberItemsHorizontally_;
+				}
+				else
+				{
+					widthInItemCount = labelCount;
+				}
+			}
+			else if (extendingHorizontally)
+			{
+				widthInItemCount = labelCount / numberItemsVertically_;
+				if (labelCount % numberItemsVertically_ != 0)
+					widthInItemCount += 1;
+			}
+			else
+			{
+				throw new NPlotException( "logic error in legend base" );
+			}
+
+
+			// determine height of legend in items count units.
+			int heightInItemCount = 0;
+			if (extendingHorizontally)
+			{
+				if (labelCount >= numberItemsVertically_)
+				{
+					heightInItemCount = numberItemsVertically_;
+				}
+				else
+				{
+					heightInItemCount = labelCount;
+				}
+			}
+			else // extendingVertically
+			{
+				heightInItemCount = labelCount / numberItemsHorizontally_;
+				if (labelCount % numberItemsHorizontally_ != 0)
+					heightInItemCount += 1;
+			}
+
+			int lineLength = 20;
+			int hSpacing = (int)(5.0f * scale);
+			int vSpacing = (int)(3.0f * scale);
+			int boxWidth = (int) ((float)widthInItemCount * (lineLength + maxWd + hSpacing * 2.0f ) + hSpacing); 
+			int boxHeight = (int)((float)heightInItemCount * (maxHt + vSpacing) + vSpacing);
+
+			int totalWidth = boxWidth;
+			int totalHeight = boxHeight;
+
+			// draw box around the legend.
+
+			if ( this.BorderStyle == BorderType.Line )
+			{
+				g.FillRectangle( new SolidBrush( this.bgColor_ ), position.X, position.Y, boxWidth, boxHeight );
+				g.DrawRectangle( new Pen( this.borderColor_ ), position.X, position.Y, boxWidth, boxHeight );
+			}
+			else if ( this.BorderStyle == BorderType.Shadow )
+			{
+				int offset = (int)(4.0f * scale);
+				g.FillRectangle( new SolidBrush( Color.FromArgb(128, Color.Gray) ), position.X+offset, position.Y+offset, boxWidth, boxHeight );
+				g.FillRectangle( new SolidBrush( this.bgColor_ ), position.X, position.Y, boxWidth, boxHeight );
+				g.DrawRectangle( new Pen( this.borderColor_ ), position.X, position.Y, boxWidth, boxHeight );
+
+				totalWidth += offset;
+				totalHeight += offset;
+			}
+
+			/*
+			   else if ( this.BorderStyle == BorderType.Curved )
+			   {
+				   // TODO. make this nice.
+			   }
+			*/
+
+			else
+			{
+				// do nothing.
+			}
+
+			// now draw entries in box..
+			labelCount = 0;
+			unnamedCount = 0;
+
+			int plotCount = -1;
+			for (int i=0; i<plots.Count; ++i)
+			{
+				if (!(plots[i] is IPlot))
+				{
+					continue;
+				}
+
+				IPlot p = (IPlot)plots[i];
+
+				if (!p.ShowInLegend)
+				{
+					continue;
+				}
+
+				plotCount += 1;
+
+				int xpos, ypos;
+				if (extendingVertically)
+				{
+					xpos = plotCount % numberItemsHorizontally_;
+					ypos = plotCount / numberItemsHorizontally_;
+				}
+				else
+				{
+					xpos = plotCount / numberItemsVertically_;
+					ypos = plotCount % numberItemsVertically_;
+				}
+
+				int lineXPos = (int)(position.X + hSpacing + xpos * (lineLength + maxWd + hSpacing * 2.0f));
+				int lineYPos = (int)(position.Y + vSpacing + ypos * (vSpacing + maxHt));
+				p.DrawInLegend( g, new Rectangle( lineXPos, lineYPos, lineLength, maxHt ) );
+				
+				int textXPos = lineXPos + hSpacing + lineLength;
+				int textYPos = lineYPos;
+				string label = p.Label;
+				if (label == "")
+				{
+					unnamedCount += 1;
+					label = "Series " + unnamedCount.ToString();
+				}
+
+				g.DrawString( label, textFont,
+					new SolidBrush( this.textColor_ ), textXPos, textYPos );
+
+				++labelCount;
+			}
+			return new Rectangle( position.X, position.Y, totalWidth, totalHeight );
+
+		}
+
+
+		/// <summary>
+		/// The font used to draw text in the legend.
+		/// </summary>
+		public Font Font
+		{
+			get
+			{
+				return this.font_;
+			}
+			set
+			{
+				this.font_ = value;
+			}
+		}
+		private Font font_;
+
+
+		/// <summary>
+		/// The color used to draw text in the legend.
+		/// </summary>
+		public Color TextColor
+		{
+			get
+			{
+				return this.textColor_;
+			}
+			set
+			{
+				this.textColor_ = value;
+			}
+		}
+		Color textColor_;
+
+
+		/// <summary>
+		/// The background color of the legend.
+		/// </summary>
+		public Color BackgroundColor
+		{
+			get
+			{
+				return bgColor_;
+			}
+			set
+			{
+				bgColor_ = value;
+			}
+		}
+		Color bgColor_;
+
+
+		/// <summary>
+		/// The color of the legend border.
+		/// </summary>
+		public Color BorderColor
+		{
+			get
+			{
+				return borderColor_;
+			}
+			set
+			{
+				borderColor_ = value;
+			}
+		}
+		Color borderColor_;
+
+
+		/// <summary>
+		/// The types of legend borders (enum).
+		/// </summary>
+		public enum BorderType
+		{
+			/// <summary>
+			/// No border.
+			/// </summary>
+			None = 0,
+			/// <summary>
+			/// Line border.
+			/// </summary>
+			Line = 1,
+			/// <summary>
+			/// Shaded border.
+			/// </summary>
+			Shadow = 2
+			//Curved = 3
+		}
+
+
+		/// <summary>
+		/// The border style to use for the legend.
+		/// </summary>
+		public Legend.BorderType BorderStyle
+		{
+			get
+			{
+				return borderStyle_;
+			}
+			set
+			{
+				borderStyle_ = value;
+			}
+		}
+		private NPlot.Legend.BorderType borderStyle_;
+
+
+		/// <summary>
+		/// Whether or not to auto scale text in the legend according the physical
+		/// dimensions of the plot surface.
+		/// </summary>
+		public bool AutoScaleText
+		{
+			get
+			{
+				return autoScaleText_;
+			}
+			set
+			{
+				autoScaleText_ = value;
+			}
+		}
+		bool autoScaleText_;
+
+
+		/// <summary>
+		/// Setting this does two things. First of all, it sets the maximum number of 
+		/// items in the legend vertically. Second of all, it makes the legend grow
+		/// horizontally (as it must given this constraint).
+		/// </summary>
+		public int NumberItemsVertically
+		{
+			set
+			{
+				this.numberItemsVertically_ = value;
+				this.numberItemsHorizontally_ = -1;
+			}
+		}
+		int numberItemsVertically_ = -1;
+
+
+		/// <summary>
+		/// Setting this does two things. First of all, it sets the maximum number of 
+		/// items in the legend horizontally. Second of all, it makes the legend grow
+		/// vertically (as it must given this constraint).
+		/// </summary>
+		public int NumberItemsHorizontally
+		{
+			set
+			{
+				this.numberItemsHorizontally_ = value;
+				this.numberItemsVertically_ = -1;
+			}
+		}
+		int numberItemsHorizontally_ = 1;
+
+	}
+
+}
diff --git a/nplot/nplot/LinePlot.cs b/nplot/nplot/LinePlot.cs
new file mode 100644
index 0000000..e7cf006
--- /dev/null
+++ b/nplot/nplot/LinePlot.cs
@@ -0,0 +1,355 @@
+/*
+NPlot - A charting library for .NET
+
+LinePlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Diagnostics;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Encapsulates functionality for plotting data as a line chart.
+	/// </summary>
+	public class LinePlot : BaseSequencePlot, IPlot, ISequencePlot
+	{
+
+		/// <summary>
+		/// Default constructor
+		/// </summary>
+		public LinePlot()
+		{
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="dataSource">The data source to associate with this plot</param>
+		public LinePlot( object dataSource )
+		{
+			this.DataSource = dataSource;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="ordinateData">the ordinate data to associate with this plot.</param>
+		/// <param name="abscissaData">the abscissa data to associate with this plot.</param>
+		public LinePlot( object ordinateData, object abscissaData )
+		{
+			this.OrdinateData = ordinateData;
+			this.AbscissaData = abscissaData;
+		}
+
+
+		/// <summary>
+		/// Draws the line plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		/// <param name="drawShadow">If true draw the shadow for the line. If false, draw line.</param>
+		public void DrawLineOrShadow( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis, bool drawShadow )
+		{
+			Pen shadowPen = null;
+			if (drawShadow)
+			{
+				shadowPen = (Pen)this.Pen.Clone();
+				shadowPen.Color = this.ShadowColor;
+			}
+
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			ITransform2D t = Transform2D.GetTransformer( xAxis, yAxis );
+			
+			int numberPoints = data.Count;
+			
+			if (data.Count == 0)
+			{
+				return;
+			}
+
+			// clipping is now handled assigning a clip region in the
+			// graphic object before this call
+			if (numberPoints == 1)
+			{
+				PointF physical = t.Transform( data[0] );
+				
+				if (drawShadow)
+				{
+					g.DrawLine( shadowPen, 
+						physical.X - 0.5f + this.ShadowOffset.X,
+						physical.Y + this.ShadowOffset.Y,
+						physical.X + 0.5f + this.ShadowOffset.X,
+						physical.Y + this.ShadowOffset.Y );
+				}
+				else
+				{
+					g.DrawLine( Pen, physical.X-0.5f, physical.Y, physical.X+0.5f, physical.Y);
+				}
+			}
+			else
+			{
+
+                // prepare for clipping
+                double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false);
+                double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false);
+				if (leftCutoff > rightCutoff)
+				{
+					Utils.Swap(ref leftCutoff, ref rightCutoff);
+				}
+				if (drawShadow)
+                {
+                    // correct cut-offs
+                    double shadowCorrection =
+                        xAxis.PhysicalToWorld(ShadowOffset, false) - xAxis.PhysicalToWorld(new Point(0,0), false);
+                    leftCutoff -= shadowCorrection;
+                    rightCutoff -= shadowCorrection;
+                }
+
+                for (int i = 1; i < numberPoints; ++i)
+				{
+					// check to see if any values null. If so, then continue.
+					double dx1 = data[i-1].X;
+					double dx2 = data[i].X;
+					double dy1 = data[i-1].Y;
+					double dy2 = data[i].Y;
+					if ( Double.IsNaN(dx1) || Double.IsNaN(dy1) ||
+						Double.IsNaN(dx2) || Double.IsNaN(dy2) )
+					{
+						continue;
+					}
+
+                    // do horizontal clipping here, to speed up
+                    if ((dx1 < leftCutoff || rightCutoff < dx1) &&
+                        (dx2 < leftCutoff || rightCutoff < dx2))
+                    {
+                        continue;
+                    }
+
+					// else draw line.
+					PointF p1 = t.Transform( data[i-1] );
+					PointF p2 = t.Transform( data[i] );
+                    
+                    // when very far zoomed in, points can fall ontop of each other,
+                    // and g.DrawLine throws an overflow exception
+                    if (p1.Equals(p2))
+                        continue;
+
+					if (drawShadow)
+					{
+						g.DrawLine( shadowPen, 
+							p1.X + ShadowOffset.X,
+							p1.Y + ShadowOffset.Y,
+							p2.X + ShadowOffset.X,
+							p2.Y + ShadowOffset.Y );
+					}
+					else
+					{
+						g.DrawLine( Pen, p1.X, p1.Y, p2.X, p2.Y );
+					}
+				}
+			}
+
+		}
+
+
+		/// <summary>
+		/// Draws the line plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			if (this.shadow_)
+			{
+				this.DrawLineOrShadow( g, xAxis, yAxis, true );
+			}
+
+			this.DrawLineOrShadow( g, xAxis, yAxis, false );
+		}
+
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			SequenceAdapter data_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			return data_.SuggestXAxis();
+		}
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+			SequenceAdapter data_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			return data_.SuggestYAxis();
+		}
+
+
+		/// <summary>
+		/// If true, draw a shadow under the line.
+		/// </summary>
+		public bool Shadow
+		{
+			get
+			{
+				return shadow_;
+			}
+			set
+			{
+				shadow_ = value;
+			}
+		}
+		private bool shadow_ = false;
+	
+
+		/// <summary>
+		/// Color of line shadow if drawn. Use Shadow method to turn shadow on and off.
+		/// </summary>
+		public Color ShadowColor
+		{
+			get
+			{
+				return shadowColor_;
+			}
+			set
+			{
+				shadowColor_ = value;
+			}
+		}
+		private Color shadowColor_ = Color.FromArgb(100,100,100);
+
+
+		/// <summary>
+		/// Offset of shadow line from primary line.
+		/// </summary>
+		public Point ShadowOffset
+		{
+			get
+			{
+				return shadowOffset_;
+			}
+			set
+			{
+				shadowOffset_ = value;
+			}
+		}
+		private Point shadowOffset_ = new Point( 1, 1 );
+
+
+        /// <summary>
+        /// Draws a representation of this plot in the legend.
+        /// </summary>
+        /// <param name="g">The graphics surface on which to draw.</param>
+        /// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+        public virtual void DrawInLegend(Graphics g, Rectangle startEnd)
+        {
+            g.DrawLine(pen_, startEnd.Left, (startEnd.Top + startEnd.Bottom) / 2,
+                startEnd.Right, (startEnd.Top + startEnd.Bottom) / 2);
+        }
+
+
+        /// <summary>
+        /// The pen used to draw the plot
+        /// </summary>
+        public System.Drawing.Pen Pen
+        {
+            get
+            {
+                return pen_;
+            }
+            set
+            {
+                pen_ = value;
+            }
+        }
+        private System.Drawing.Pen pen_ = new Pen(Color.Black);
+
+
+        /// <summary>
+        /// The color of the pen used to draw lines in this plot.
+        /// </summary>
+        public System.Drawing.Color Color
+        {
+            set
+            {
+                if (pen_ != null)
+                {
+                    pen_.Color = value;
+                }
+                else
+                {
+                    pen_ = new Pen(value);
+                }
+            }
+            get
+            {
+                return pen_.Color;
+            }
+        }
+
+    
+    }
+}
diff --git a/nplot/nplot/LinearAxis.cs b/nplot/nplot/LinearAxis.cs
new file mode 100644
index 0000000..57860ab
--- /dev/null
+++ b/nplot/nplot/LinearAxis.cs
@@ -0,0 +1,650 @@
+/*
+NPlot - A charting library for .NET
+
+LinearAxis.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Drawing;
+using System.Collections;
+using System;
+using System.Text;
+using System.Diagnostics;
+
+namespace NPlot
+{
+	/// <summary>
+	/// Provides functionality for drawing axes with a linear numeric scale.
+	/// </summary>
+	public class LinearAxis : Axis, System.ICloneable
+	{
+
+		/// <summary>
+		/// Deep copy of LinearAxis.
+		/// </summary>
+		/// <returns>A copy of the LinearAxis Class</returns>
+		public override object Clone()
+		{
+			LinearAxis a = new LinearAxis();
+			// ensure that this isn't being called on a derived type. If it is, then oh no!
+			if (this.GetType() != a.GetType())
+			{
+				throw new NPlotException( "Clone not defined in derived type. Help!" );
+			}
+			this.DoClone( this, a );
+			return a;
+		}
+
+
+		/// <summary>
+		/// Helper method for Clone.
+		/// </summary>
+		protected void DoClone( LinearAxis b, LinearAxis a )
+		{
+			Axis.DoClone( b, a );
+
+			a.numberSmallTicks_ = b.numberSmallTicks_;
+			a.largeTickValue_ = b.largeTickValue_;
+			a.largeTickStep_ = b.largeTickStep_;
+
+			a.offset_ = b.offset_;
+			a.scale_ = b.scale_;
+		}
+
+
+		/// <summary>
+		/// Copy constructor
+		/// </summary>
+		/// <param name="a">The Axis to clone</param>
+		public LinearAxis( Axis a )
+			: base( a )
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Default constructor.
+		/// </summary>
+		public LinearAxis()
+			: base()
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Construct a linear axis with the provided world min and max values.
+		/// </summary>
+		/// <param name="worldMin">the world minimum value of the axis.</param>
+		/// <param name="worldMax">the world maximum value of the axis.</param>
+		public LinearAxis( double worldMin, double worldMax )
+			: base( worldMin, worldMax )
+		{
+			Init();
+		}
+
+
+		private void Init() 
+		{
+			this.NumberFormat = "{0:g5}";
+		}
+
+
+		/// <summary>
+		/// Draws the large and small ticks [and tick labels] for this axis.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="boundingBox">out: smallest box that completely surrounds all ticks and associated labels for this axis.</param>
+		/// <param name="labelOffset">out: offset from the axis to draw the axis label.</param>
+		protected override void DrawTicks(
+			Graphics g, 
+			Point physicalMin, 
+			Point physicalMax, 
+			out object labelOffset,
+			out object boundingBox )
+		{
+
+			Point tLabelOffset;
+			Rectangle tBoundingBox;
+
+			labelOffset = this.getDefaultLabelOffset( physicalMin, physicalMax );
+			boundingBox = null;
+
+			ArrayList largeTickPositions;
+			ArrayList smallTickPositions;
+			this.WorldTickPositions( physicalMin, physicalMax, out largeTickPositions, out smallTickPositions );
+
+			labelOffset = new Point( 0, 0 );
+			boundingBox = null;
+
+			if (largeTickPositions.Count > 0)
+			{
+				for (int i = 0; i < largeTickPositions.Count; ++i)
+				{
+					double labelNumber = (double)largeTickPositions[i];
+
+					// TODO: Find out why zero is sometimes significantly not zero [seen as high as 10^-16].
+					if (Math.Abs(labelNumber) < 0.000000000000001)
+					{
+						labelNumber = 0.0;
+					}
+
+					StringBuilder label = new StringBuilder();
+					label.AppendFormat(this.NumberFormat, labelNumber);
+
+					this.DrawTick( g, ((double)largeTickPositions[i]/this.scale_-this.offset_), 
+						this.LargeTickSize, label.ToString(),
+						new Point(0,0), physicalMin, physicalMax, 
+						out tLabelOffset, out tBoundingBox );
+					
+					Axis.UpdateOffsetAndBounds( ref labelOffset, ref boundingBox, 
+						tLabelOffset, tBoundingBox );
+
+				}
+			}
+
+			for (int i = 0; i<smallTickPositions.Count; ++i)
+			{
+				this.DrawTick( g, ((double)smallTickPositions[i]/this.scale_-this.offset_), 
+					this.SmallTickSize, "", 
+					new Point(0, 0), physicalMin, physicalMax, 
+					out tLabelOffset, out tBoundingBox );
+
+				// assume bounding box and label offset unchanged by small tick bounds.
+			}
+
+		}
+
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the small ticks
+		/// if they have not already been generated.
+		/// 
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">The positions of the large ticks.</param>
+		/// <param name="smallTickPositions">If null, small tick positions are returned via this parameter. Otherwise this function does nothing.</param>
+		internal override void WorldTickPositions_SecondPass( 
+			Point physicalMin,
+			Point physicalMax,
+			ArrayList largeTickPositions, 
+			ref ArrayList smallTickPositions )
+		{
+
+			// return if already generated.
+			if (smallTickPositions != null)
+				return;
+
+			int physicalAxisLength = Utils.Distance( physicalMin, physicalMax );
+
+			double adjustedMax = this.AdjustedWorldValue( WorldMax );
+			double adjustedMin = this.AdjustedWorldValue( WorldMin );
+
+			smallTickPositions = new ArrayList();
+
+			// TODO: Can optimize this now.
+			bool shouldCullMiddle;
+			double bigTickSpacing = this.DetermineLargeTickStep( physicalAxisLength, out shouldCullMiddle );
+
+			int nSmall = this.DetermineNumberSmallTicks( bigTickSpacing );
+			double smallTickSpacing = bigTickSpacing / (double)nSmall;
+
+			// if there is at least one big tick
+			if (largeTickPositions.Count > 0)
+			{
+				double pos1 = (double)largeTickPositions[0] - smallTickSpacing;
+				while (pos1 > adjustedMin)
+				{
+					smallTickPositions.Add( pos1 );
+					pos1 -= smallTickSpacing;
+				}
+			}
+
+			for (int i = 0; i < largeTickPositions.Count; ++i )
+			{
+				for (int j = 1; j < nSmall; ++j )
+				{
+					double pos = (double)largeTickPositions[i] + ((double)j) * smallTickSpacing;
+					if (pos <= adjustedMax)
+					{
+						smallTickPositions.Add( pos );
+					}
+				}
+			}
+
+		}
+
+		/// <summary>
+		/// Adjusts a real world value to one that has been modified to
+		/// reflect the Axis Scale and Offset properties.
+		/// </summary>
+		/// <param name="world">world value to adjust</param>
+		/// <returns>adjusted world value</returns>
+		public double AdjustedWorldValue( double world )
+		{
+			return world * this.scale_ + this.offset_;
+		}
+
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the large ticks. 
+		/// When the physical extent of the axis is small, some of the positions 
+		/// that were generated in this pass may be converted to small tick 
+		/// positions and returned as well.
+		///
+		/// If the LargeTickStep isn't set then this is calculated automatically and
+		/// depends on the physical extent of the axis. 
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">ArrayList containing the positions of the small ticks if calculated, null otherwise.</param>
+		internal override void WorldTickPositions_FirstPass(
+			Point physicalMin, 
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+
+			// (1) error check
+
+			if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
+			{
+				throw new NPlotException( "world extent of axis not set." );
+			}
+			
+			double adjustedMax = this.AdjustedWorldValue( WorldMax );
+			double adjustedMin = this.AdjustedWorldValue( WorldMin );
+
+			// (2) determine distance between large ticks.
+			bool shouldCullMiddle;
+			double tickDist = this.DetermineLargeTickStep( 
+				Utils.Distance(physicalMin, physicalMax),
+				out shouldCullMiddle );
+
+			// (3) determine starting position.
+		
+			double first = 0.0f;
+
+			if (!double.IsNaN(largeTickValue_)) 
+			{
+				// this works for both case when largTickValue_ lt or gt adjustedMin.
+				first = largeTickValue_ + (Math.Ceiling((adjustedMin-largeTickValue_)/tickDist))*tickDist;
+			}
+
+			else
+			{
+				if( adjustedMin > 0.0 )
+				{
+					double nToFirst = Math.Floor(adjustedMin / tickDist) + 1.0f;
+					first = nToFirst * tickDist;
+				}
+				else
+				{
+					double nToFirst = Math.Floor(-adjustedMin/tickDist) - 1.0f;
+					first = -nToFirst * tickDist;
+				}
+
+				// could miss one, if first is just below zero.
+				if ((first - tickDist) >= adjustedMin)
+				{
+					first -= tickDist;
+				}
+			}
+
+
+			// (4) now make list of large tick positions.
+			
+			largeTickPositions = new ArrayList();
+
+			if (tickDist < 0.0) // some sanity checking. TODO: remove this.
+				throw new NPlotException( "Tick dist is negative" );
+
+			double position = first;
+			int safetyCount = 0;
+			while (
+				(position <= adjustedMax) && 
+				(++safetyCount < 5000) )
+			{
+				largeTickPositions.Add( position );
+				position += tickDist;
+			}
+
+			// (5) if the physical extent is too small, and the middle 
+			// ticks should be turned into small ticks, then do this now.
+			smallTickPositions = null;
+			if (shouldCullMiddle)
+			{
+				smallTickPositions = new ArrayList();
+
+				if (largeTickPositions.Count > 2)
+				{
+					for (int i=1; i<largeTickPositions.Count-1; ++i)
+					{
+						smallTickPositions.Add( largeTickPositions[i] );
+					}
+				}
+
+				ArrayList culledPositions = new ArrayList();
+				culledPositions.Add( largeTickPositions[0] );
+				culledPositions.Add( largeTickPositions[largeTickPositions.Count-1] );
+				largeTickPositions = culledPositions;
+			}
+
+		}
+
+
+		/// <summary>
+		/// Calculates the world spacing between large ticks, based on the physical
+		/// axis length (parameter), world axis length, Mantissa values and 
+		/// MinPhysicalLargeTickStep. A value such that at least two 
+		/// </summary>
+		/// <param name="physicalLength">physical length of the axis</param>
+		/// <param name="shouldCullMiddle">Returns true if we were forced to make spacing of 
+		/// large ticks too small in order to ensure that there are at least two of 
+		/// them. The draw ticks method should not draw more than two large ticks if this
+		/// returns true.</param>
+		/// <returns>Large tick spacing</returns>
+		/// <remarks>TODO: This can be optimised a bit.</remarks>
+		private double DetermineLargeTickStep( float physicalLength, out bool shouldCullMiddle )
+		{
+			shouldCullMiddle = false;
+
+			if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
+			{
+				throw new NPlotException( "world extent of axis not set." );
+			}
+
+			// if the large tick has been explicitly set, then return this.
+			if ( !double.IsNaN(largeTickStep_) )
+			{
+				if ( largeTickStep_ <= 0.0f )
+				{
+					throw new NPlotException( 
+						"can't have negative or zero tick step - reverse WorldMin WorldMax instead."
+					);
+				}
+				return largeTickStep_;
+			}
+
+			// otherwise we need to calculate the large tick step ourselves.
+
+			// adjust world max and min for offset and scale properties of axis.
+			double adjustedMax = this.AdjustedWorldValue( WorldMax );
+			double adjustedMin = this.AdjustedWorldValue( WorldMin );
+			double range = adjustedMax - adjustedMin;
+
+			// if axis has zero world length, then return arbitrary number.
+			if ( Utils.DoubleEqual( adjustedMax, adjustedMin ) )
+			{
+				return 1.0f;
+			}
+
+			double approxTickStep;
+			if (TicksIndependentOfPhysicalExtent)
+			{
+				approxTickStep = range / 6.0f;
+			}
+			else
+			{
+				approxTickStep = (MinPhysicalLargeTickStep / physicalLength) * range;
+			}
+
+			double exponent = Math.Floor( Math.Log10( approxTickStep ) );
+			double mantissa = Math.Pow( 10.0, Math.Log10( approxTickStep ) - exponent );
+
+			// determine next whole mantissa below the approx one.
+			int mantissaIndex = Mantissas.Length-1;
+			for (int i=1; i<Mantissas.Length; ++i)
+			{
+				if (mantissa < Mantissas[i])
+				{
+					mantissaIndex = i-1;
+					break;
+				}
+			}
+			
+			// then choose next largest spacing. 
+			mantissaIndex += 1;
+			if (mantissaIndex == Mantissas.Length)
+			{
+				mantissaIndex = 0;
+				exponent += 1.0;
+			}
+
+			if (!TicksIndependentOfPhysicalExtent)
+			{
+				// now make sure that the returned value is such that at least two 
+				// large tick marks will be displayed.
+				double tickStep = Math.Pow( 10.0, exponent ) * Mantissas[mantissaIndex];
+				float physicalStep = (float)((tickStep / range) * physicalLength);
+
+				while (physicalStep > physicalLength/2)
+				{
+					shouldCullMiddle = true;
+
+					mantissaIndex -= 1;
+					if (mantissaIndex == -1)
+					{
+						mantissaIndex = Mantissas.Length-1;
+						exponent -= 1.0;
+					}
+
+					tickStep = Math.Pow( 10.0, exponent ) * Mantissas[mantissaIndex];
+					physicalStep = (float)((tickStep / range) * physicalLength);
+				}
+			}
+
+			// and we're done.
+			return Math.Pow( 10.0, exponent ) * Mantissas[mantissaIndex];
+
+		}
+
+
+		/// <summary>
+		/// Given the large tick step, determine the number of small ticks that should
+		/// be placed in between.
+		/// </summary>
+		/// <param name="bigTickDist">the large tick step.</param>
+		/// <returns>the number of small ticks to place between large ticks.</returns>
+		private int DetermineNumberSmallTicks( double bigTickDist )
+		{
+
+			if (this.numberSmallTicks_ != null)
+			{
+				return (int)this.numberSmallTicks_+1;
+			}
+
+			if (this.SmallTickCounts.Length != this.Mantissas.Length)
+			{
+				throw new NPlotException( "Mantissa.Length != SmallTickCounts.Length" );
+			}
+
+			if (bigTickDist > 0.0f)
+			{
+
+				double exponent = Math.Floor( Math.Log10( bigTickDist ) );
+				double mantissa = Math.Pow( 10.0, Math.Log10( bigTickDist ) - exponent );
+
+				for (int i=0; i<Mantissas.Length; ++i)
+				{
+					if ( Math.Abs(mantissa-Mantissas[i]) < 0.001 )
+					{
+						return SmallTickCounts[i]+1;
+					}
+				}
+
+			}
+				
+			return 0;
+
+		}
+
+
+		/// <summary>
+		/// The distance between large ticks. If this is set to NaN [default],
+		/// this distance will be calculated automatically.
+		/// </summary>
+		public double LargeTickStep
+		{
+			set
+			{
+				largeTickStep_ = value;
+			}
+			get
+			{
+				return largeTickStep_;
+			}
+		}
+		/// <summary>
+		/// If set !NaN, gives the distance between large ticks.
+		/// </summary>
+		private double largeTickStep_ = double.NaN;
+
+
+		/// <summary>
+		/// If set, a large tick will be placed at this position, and other large ticks will 
+		/// be placed relative to this position.
+		/// </summary>
+		public double LargeTickValue
+		{
+			set
+			{
+				largeTickValue_ = value;
+			}
+			get
+			{
+				return largeTickValue_;
+			}
+		}
+		private double largeTickValue_ = double.NaN;
+
+		/// <summary>
+		/// The number of small ticks between large ticks.
+		/// </summary>
+		public int NumberOfSmallTicks
+		{
+			set
+			{
+				numberSmallTicks_ = value;
+			}
+			get
+			{
+				// TODO: something better here.
+				return (int)numberSmallTicks_;
+			}
+		}
+		private object numberSmallTicks_ = null;
+
+		/// <summary>
+		/// Scale to apply to world values when labelling axis:
+		/// (labelWorld = world * scale + offset). This does not
+		/// affect the "real" world range of the axis. 
+		/// </summary>
+		public double Scale
+		{
+			get
+			{
+				return scale_;
+			}
+			set
+			{
+				scale_ = value;
+			}
+		}
+
+		/// <summary>
+		/// Offset to apply to world values when labelling the axis:
+		/// (labelWorld = axisWorld * scale + offset). This does not
+		/// affect the "real" world range of the axis.
+		/// </summary>
+		public double Offset
+		{
+			get
+			{
+				return offset_;
+			}
+			set
+			{
+				offset_ = value;
+			}
+		}
+
+		/// <summary>
+		/// If LargeTickStep isn't specified, then a suitable value is 
+		/// calculated automatically. To determine the tick spacing, the
+		/// world axis length is divided by ApproximateNumberLargeTicks
+		/// and the next lowest distance m*10^e for some m in the Mantissas
+		/// set and some integer e is used as the large tick spacing. 
+		/// </summary>
+		public float ApproxNumberLargeTicks = 3.0f;
+
+		/// <summary>
+		/// If LargeTickStep isn't specified, then a suitable value is
+		/// calculated automatically. The value will be of the form
+		/// m*10^e for some m in this set.
+		/// </summary>
+		public double[] Mantissas = {1.0, 2.0, 5.0};
+
+		/// <summary>
+		/// If NumberOfSmallTicks isn't specified then .... 
+		/// If specified LargeTickStep manually, then no small ticks unless
+		/// NumberOfSmallTicks specified.
+		/// </summary>
+		public int[] SmallTickCounts = {4, 1, 4};
+
+
+		private double offset_ = 0.0;
+
+		private double scale_ = 1.0;
+	}
+}
diff --git a/nplot/nplot/LinearGradient.cs b/nplot/nplot/LinearGradient.cs
new file mode 100644
index 0000000..df5dd7c
--- /dev/null
+++ b/nplot/nplot/LinearGradient.cs
@@ -0,0 +1,159 @@
+/*
+NPlot - A charting library for .NET
+
+Gradient.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// Class for creating a linear gradient.
+	/// </summary>
+	public class LinearGradient : IGradient
+	{
+		
+		/// <summary>
+		/// Constructor.
+		/// </summary>
+		/// <param name="minColor">The color corresponding to 0.0</param>
+		/// <param name="maxColor">The color corresponding to 1.0</param>
+		public LinearGradient( Color minColor, Color maxColor )
+		{
+			this.minColor_ = minColor;
+			this.maxColor_ = maxColor;
+		}
+
+
+		/// <summary>
+		/// The color corresponding to 0.0
+		/// </summary>
+		public Color MaxColor
+		{
+			get
+			{
+				return this.maxColor_;
+			}
+			set
+			{
+				this.maxColor_ = value;
+			}
+		}
+		private Color maxColor_;
+
+
+		/// <summary>
+		/// The color corresponding to 1.0
+		/// </summary>
+		public Color MinColor
+		{
+			get
+			{
+				return this.minColor_;
+			}
+			set
+			{
+				this.minColor_ = value;
+			}
+		}
+		private Color minColor_;
+
+
+		/// <summary>
+		/// The color corresponding to NaN
+		/// </summary>
+		public Color VoidColor			         
+		{
+			get 
+			{ 
+				return voidColor_; 
+			}
+			set 
+			{ 
+				voidColor_ = value; 
+			}
+		}
+        private Color voidColor_ = Color.Black;
+
+
+		/// <summary>
+		/// Gets a color corresponding to a number between 0.0 and 1.0 inclusive. The color will
+		/// be a linear interpolation of the min and max colors.
+		/// </summary>
+		/// <param name="prop">the number to get corresponding color for (between 0.0 and 1.0)</param>
+		/// <returns>The color corresponding to the supplied number.</returns>
+		public Color GetColor( double prop )
+		{
+            if (Double.IsNaN(prop))
+            {
+                return voidColor_;
+            }
+
+			if ( prop <= 0.0 )
+			{
+				return this.MinColor;
+			}
+
+			if ( prop >= 1.0 )
+			{
+				return this.MaxColor;
+			}
+
+			byte r = (byte)((int)(this.MinColor.R) + (int)(((double)this.MaxColor.R - (double)this.MinColor.R)*prop));
+			byte g = (byte)((int)(this.MinColor.G) + (int)(((double)this.MaxColor.G - (double)this.MinColor.G)*prop));
+			byte b = (byte)((int)(this.MinColor.B) + (int)(((double)this.MaxColor.B - (double)this.MinColor.B)*prop));
+
+			return Color.FromArgb(r,g,b);
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/LogAxis.cs b/nplot/nplot/LogAxis.cs
new file mode 100644
index 0000000..9ed2fa7
--- /dev/null
+++ b/nplot/nplot/LogAxis.cs
@@ -0,0 +1,678 @@
+/*
+NPlot - A charting library for .NET
+
+LogAxis.cs
+Copyright (C) 2003
+Paolo Pierini, Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Collections;
+using System.Text;
+
+namespace NPlot
+{
+	/// <summary>
+	/// The class implementing logarithmic axes.
+	/// </summary>
+	public class LogAxis : Axis
+	{
+
+		/// <summary>
+		/// Deep Copy of the LogAxis.
+		/// </summary>
+		/// <returns>A Copy of the LogAxis Class.</returns>
+		public override object Clone()
+		{
+			LogAxis a = new LogAxis();
+			if (this.GetType() != a.GetType())
+			{
+				throw new NPlotException("Clone not defined in derived type. Help!");
+			}
+			this.DoClone( this, a );
+			return a;
+		}
+
+
+		/// <summary>
+		/// Helper method for Clone (actual implementation)
+		/// </summary>
+		/// <param name="a">The original object to clone.</param>
+		/// <param name="b">The cloned object.</param>
+		protected void DoClone(LogAxis b, LogAxis a)
+		{
+			Axis.DoClone(b,a);
+			// add specific elemtents of the class for the deep copy of the object
+			a.numberSmallTicks_ = b.numberSmallTicks_;
+			a.largeTickValue_ = b.largeTickValue_;
+			a.largeTickStep_ = b.largeTickStep_;
+		}
+
+
+		/// <summary>
+		/// Default constructor.
+		/// </summary>
+		public LogAxis()
+			: base()
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Copy Constructor
+		/// </summary>
+		/// <param name="a">The Axis to clone.</param>
+		public LogAxis(Axis a)
+			: base(a)
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="worldMin">Minimum World value for the axis.</param>
+		/// <param name="worldMax">Maximum World value for the axis.</param>
+		public LogAxis(double worldMin, double worldMax)
+			: base( worldMin, worldMax )
+		{
+			Init();
+		}
+
+
+		private void Init() 
+		{
+			this.NumberFormat = "{0:g5}";
+		}
+
+
+		/// <summary>
+		/// Draw the ticks.
+		/// </summary>
+		/// <param name="g">The drawing surface on which to draw.</param>
+		/// <param name="physicalMin">The minimum physical extent of the axis.</param>
+		/// <param name="physicalMax">The maximum physical extent of the axis.</param>
+		/// <param name="boundingBox">out: smallest box that completely encompasses all of the ticks and tick labels.</param>
+		/// <param name="labelOffset">out: a suitable offset from the axis to draw the axis label.</param>
+		/// <returns> An ArrayList containing the offset from the axis required for an axis label
+		/// to miss this tick, followed by a bounding rectangle for the tick and tickLabel drawn.</returns>
+		protected override void DrawTicks(
+			Graphics g, 
+			Point physicalMin, 
+			Point physicalMax, 
+			out object labelOffset,
+			out object boundingBox )
+		{
+
+			Point tLabelOffset;
+			Rectangle tBoundingBox;
+
+			labelOffset = this.getDefaultLabelOffset( physicalMin, physicalMax );
+			boundingBox = null;
+
+			ArrayList largeTickPositions;
+			ArrayList smallTickPositions;
+			this.WorldTickPositions( physicalMin, physicalMax, out largeTickPositions, out smallTickPositions );
+
+			Point offset = new Point( 0, 0 );
+			object bb = null;
+			// Missed this protection
+			if (largeTickPositions.Count > 0)
+			{
+				for (int i=0; i<largeTickPositions.Count; ++i)
+				{
+					StringBuilder label = new StringBuilder();
+					// do google search for "format specifier writeline" for help on this.
+					label.AppendFormat(this.NumberFormat, (double)largeTickPositions[i]);
+					this.DrawTick( g, (double)largeTickPositions[i], this.LargeTickSize, label.ToString(),
+						new Point(0,0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox );
+
+					Axis.UpdateOffsetAndBounds( ref labelOffset, ref boundingBox, tLabelOffset, tBoundingBox );
+				}
+			}
+			else
+			{
+				// just get the axis bounding box)
+				PointF dir = Utils.UnitVector(physicalMin,physicalMax);
+				Rectangle rr = new Rectangle( physicalMin.X,
+					(int)((physicalMax.X-physicalMin.X)*dir.X),
+					physicalMin.Y,
+					(int)((physicalMax.Y-physicalMin.Y)*dir.Y) );
+				bb = rr;
+			}
+			
+
+			// missed protection for zero ticks
+			if (smallTickPositions.Count > 0)
+			{
+				for (int i=0; i<smallTickPositions.Count; ++i)
+				{
+					this.DrawTick( g, (double)smallTickPositions[i], this.SmallTickSize,
+						"", new Point(0,0), physicalMin, physicalMax, out tLabelOffset, out tBoundingBox );
+					// ignore r for now - assume bb unchanged by small tick bounds.
+				}
+			}
+
+		}
+
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the small ticks
+		/// if they have not already been generated.
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">The positions of the large ticks, unchanged</param>
+		/// <param name="smallTickPositions">If null, small tick positions are returned via this parameter. Otherwise this function does nothing.</param>
+		internal override void WorldTickPositions_SecondPass( 
+			Point physicalMin,
+			Point physicalMax,
+			ArrayList largeTickPositions, 
+			ref ArrayList smallTickPositions )
+		{
+
+			if (smallTickPositions != null)
+			{
+				throw new NPlotException( "not expecting smallTickPositions to be set already." );
+			}
+
+			smallTickPositions = new ArrayList();
+
+			// retrieve the spacing of the big ticks. Remember this is decades!
+			double bigTickSpacing = this.DetermineTickSpacing();
+			int nSmall = this.DetermineNumberSmallTicks( bigTickSpacing );
+
+			// now we have to set the ticks
+			// let us start with the easy case where the major tick distance
+			// is larger than a decade
+			if ( bigTickSpacing > 1.0f )
+			{
+				if (largeTickPositions.Count > 0)
+				{
+					// deal with the smallticks preceding the
+					// first big tick
+					double pos1 = (double)largeTickPositions[0];
+					while (pos1 > this.WorldMin)
+					{
+						pos1 = pos1 / 10.0f;
+						smallTickPositions.Add( pos1 );
+					}
+					// now go on for all other Major ticks
+					for (int i=0; i<largeTickPositions.Count; ++i )
+					{
+						double pos = (double)largeTickPositions[i];
+						for (int j=1; j<=nSmall; ++j )
+						{
+							pos=pos*10.0F;
+							// check to see if we are still in the range
+							if (pos < WorldMax)
+							{
+								smallTickPositions.Add( pos );
+							}
+						}
+					}
+				}
+			}
+			else
+			{
+				// guess what...
+				double [] m = { 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
+				// Then we deal with the other ticks
+				if (largeTickPositions.Count > 0)
+				{
+					// first deal with the smallticks preceding the first big tick
+					// positioning before the first tick
+					double pos1=(double)largeTickPositions[0]/10.0f;
+					for (int i=0; i<m.Length; i++)
+					{
+						double pos=pos1*m[i];
+						if (pos>this.WorldMin)
+						{
+							smallTickPositions.Add(pos);
+						}
+					}
+					// now go on for all other Major ticks
+					for (int i=0; i<largeTickPositions.Count; ++i )
+					{
+						pos1=(double)largeTickPositions[i];
+						for (int j=0; j<m.Length; ++j )
+						{
+							double pos=pos1*m[j];
+							// check to see if we are still in the range
+							if (pos < WorldMax)
+							{
+								smallTickPositions.Add( pos );
+							}
+						}
+					}
+				}
+				else
+				{
+					// probably a minor tick would anyway fall in the range
+					// find the decade preceding the minimum
+					double dec=Math.Floor(Math.Log10(WorldMin));
+					double pos1=Math.Pow(10.0,dec);
+					for (int i=0; i<m.Length; i++)
+					{
+						double pos=pos1*m[i];
+						if (pos>this.WorldMin && pos< this.WorldMax )
+						{
+							smallTickPositions.Add(pos);
+						}
+					}
+				}
+			}
+
+		}
+
+		private static double m_d5Log = -Math.Log10(0.5);   // .30103
+		private static double m_d5RegionPos = Math.Abs(m_d5Log + ((1 - m_d5Log) / 2)); //	   ' .6505
+		private static double m_d5RegionNeg = Math.Abs(m_d5Log / 2); //	   '.1505
+
+		private void CalcGrids( double dLenAxis, int nNumDivisions, ref double dDivisionInterval)
+		{
+			double dMyInterval  = dLenAxis / nNumDivisions;
+			double dPower = Math.Log10(dMyInterval);
+			dDivisionInterval = 10 ^ (int)dPower;
+			double dFixPower = dPower - (int)dPower;
+			double d5Region = Math.Abs(dPower - dFixPower);
+			double dMyMult;
+			if (dPower < 0)
+			{
+				d5Region = -(dPower - dFixPower);
+				dMyMult = 0.5;
+			}
+			else
+			{
+				d5Region = 1 - (dPower - dFixPower);
+				dMyMult = 5;
+			}
+			if ((d5Region >= m_d5RegionNeg) && (d5Region <= m_d5RegionPos))
+			{
+				dDivisionInterval = dDivisionInterval * dMyMult;
+			}
+		}
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the log spaced large ticks. 
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">null</param>
+		internal override void WorldTickPositions_FirstPass(
+			Point physicalMin, 
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+
+			smallTickPositions = null;
+			largeTickPositions = new ArrayList();
+
+			if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
+			{
+				throw new NPlotException( "world extent of axis not set." );
+			}
+
+			double roundTickDist = this.DetermineTickSpacing( );
+
+			// now determine first tick position.
+			double first = 0.0f;
+
+			// if the user hasn't specified a large tick position.
+			if (double.IsNaN(largeTickValue_))
+			{
+				if( WorldMin > 0.0 )
+				{
+
+					double nToFirst = Math.Floor(Math.Log10(WorldMin) / roundTickDist)+1.0f;
+					first = nToFirst * roundTickDist;
+				}
+
+				// could miss one, if first is just below zero.
+				if (first-roundTickDist >= Math.Log10(WorldMin))
+				{
+					first -= roundTickDist;
+				}
+			}
+			
+			// the user has specified one place they would like a large tick placed.
+			else
+			{
+				first = Math.Log10( this.LargeTickValue );
+
+				// TODO: check here not too much different.
+				// could result in long loop.
+				while (first < Math.Log10(WorldMin))
+				{
+					first += roundTickDist;
+				}
+
+				while (first > Math.Log10(WorldMin)+roundTickDist)
+				{
+					first -= roundTickDist;
+				}
+			}
+
+			double mark = first;
+			while (mark <= Math.Log10(WorldMax))
+			{
+				// up to here only logs are dealt with, but I want to return
+				// a real value in the arraylist
+				double val;
+				val = Math.Pow( 10.0, mark );
+				largeTickPositions.Add( val );
+				mark += roundTickDist;
+			}
+
+		}
+
+
+		/// <summary>
+		/// Determines the tick spacing.
+		/// </summary>
+		/// <returns>The tick spacing (in decades!)</returns>
+		private double DetermineTickSpacing( )
+		{
+			if ( double.IsNaN(WorldMin) || double.IsNaN(WorldMax) )
+			{
+				throw new NPlotException( "world extent of axis is not set." );
+			}
+
+			// if largeTickStep has been set, it is used
+			if ( !double.IsNaN( this.largeTickStep_) )
+			{
+				if ( this.largeTickStep_ <= 0.0f )
+				{
+					throw new NPlotException( "can't have negative tick step - reverse WorldMin WorldMax instead." );
+				}
+
+				return this.largeTickStep_;
+			}
+
+			double MagRange = (double)(Math.Floor(Math.Log10(WorldMax)) - Math.Floor(Math.Log10(WorldMin))+1.0);
+
+			if ( MagRange > 0.0 )
+			{
+				// for now, a simple logic
+				// start with a major tick every order of magnitude, and
+				// increment if in order not to have more than 10 ticks in
+				// the plot.
+				double roundTickDist=1.0F;
+				int nticks=(int)(MagRange/roundTickDist);
+				while (nticks > 10)
+				{
+					roundTickDist++;
+					nticks=(int)(MagRange/roundTickDist);
+				}
+				return roundTickDist;
+			}
+			else
+			{
+				return 0.0f;
+			}
+		}
+
+
+		/// <summary>
+		/// Determines the number of small ticks between two large ticks.
+		/// </summary>
+		/// <param name="bigTickDist">The distance between two large ticks.</param>
+		/// <returns>The number of small ticks.</returns>
+		private int DetermineNumberSmallTicks( double bigTickDist )
+		{
+			// if the big ticks is more than one decade, the
+			// small ticks are every decade, I don't let the user set it.
+			if (this.numberSmallTicks_ != null && bigTickDist == 1.0f)
+			{
+				return (int)this.numberSmallTicks_+1;
+			}
+
+			// if we are plotting every decade, we have to
+			// put the log ticks. As a start, I put every
+			// small tick (.2,.3,.4,.5,.6,.7,.8,.9)
+			if (bigTickDist == 1.0f)
+			{
+				return 8;
+			}
+				// easy, put a tick every missed decade
+			else if (bigTickDist > 1.0f)
+			{
+				return (int)bigTickDist - 1;
+			}
+			else
+			{
+				throw new NPlotException("Wrong Major tick distance setting");
+			}
+		}
+
+
+		/// <summary>
+		/// The step between large ticks, expressed in decades for the Log scale.
+		/// </summary>
+		public double LargeTickStep
+		{
+			set
+			{
+				largeTickStep_ = value;
+			}
+			get
+			{
+				return largeTickStep_;
+			}
+		}
+
+
+		/// <summary>
+		/// Position of one of the large ticks [other positions will be calculated relative to this one].
+		/// </summary>
+		public double LargeTickValue
+		{
+			set
+			{
+				largeTickValue_ = value;
+			}
+			get
+			{
+				return largeTickValue_;
+			}
+		}
+
+
+		/// <summary>
+		/// The number of small ticks between large ticks.
+		/// </summary>
+		public int NumberSmallTicks
+		{
+			set
+			{
+				numberSmallTicks_ = value;
+			}
+		}
+
+
+		// Private members
+		private object numberSmallTicks_;
+		private double largeTickValue_ = double.NaN;
+		private double largeTickStep_ = double.NaN;
+
+		/// <summary>
+		/// World to physical coordinate transform.
+		/// </summary>
+		/// <param name="coord">The coordinate value to transform.</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="clip">if false, then physical value may extend outside worldMin / worldMax. If true, the physical value returned will be clipped to physicalMin or physicalMax if it lies outside this range.</param>
+		/// <returns>The transformed coordinates.</returns>
+		/// <remarks>TODO: make Reversed property work for this.</remarks>
+		public override PointF WorldToPhysical( 
+			double coord,
+			PointF physicalMin, 
+			PointF physicalMax,
+			bool clip )
+		{
+			// if want clipped value, return extrema if outside range.
+			if (clip)
+			{
+				if (coord > WorldMax)
+				{
+					return physicalMax;
+				}
+				if (coord < WorldMin)
+				{
+					return physicalMin;
+				}
+			}
+
+			if (coord < 0.0f)
+			{
+				throw new NPlotException( "Cannot have negative values for data using Log Axis" );
+			}
+
+			// inside range or don't want to clip.
+			double lrange = (double)(Math.Log10(WorldMax) - Math.Log10(WorldMin));
+			double prop = (double)((Math.Log10(coord) - Math.Log10(WorldMin)) / lrange);
+			PointF offset = new PointF( (float)(prop * (physicalMax.X - physicalMin.X)),
+				(float)(prop * (physicalMax.Y - physicalMin.Y)) );
+
+			return new PointF( physicalMin.X + offset.X, physicalMin.Y + offset.Y );
+		}
+
+
+		/// <summary>
+		/// Return the world coordinate of the projection of the point p onto
+		/// the axis.
+		/// </summary>
+		/// <param name="p">The point to project onto the axis</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="clip">If true, the world value will be clipped to WorldMin or WorldMax as appropriate if it lies outside this range.</param>
+		/// <returns>The world value corresponding to the projection of the point p onto the axis.</returns>
+		public override double PhysicalToWorld( PointF p, PointF physicalMin, PointF physicalMax, bool clip )
+		{
+			// use the base method to do the projection on the axis.
+			double t = base.PhysicalToWorld( p, physicalMin, physicalMax, clip );
+
+			// now reconstruct phys dist prop along this assuming linear scale as base method did.
+			double v = (t - this.WorldMin) / (this.WorldMax - this.WorldMin);
+
+			double ret = WorldMin*Math.Pow( WorldMax / WorldMin, v );
+
+			// if want clipped value, return extrema if outside range.
+			if (clip)
+			{
+				ret = Math.Max( ret, WorldMin );
+				ret = Math.Min( ret, WorldMax );
+			}
+
+			return ret;
+
+		}
+
+
+		/// <summary>
+		/// The minimum world extent of the axis. Must be greater than zero.
+		/// </summary>
+		public override double WorldMin
+		{
+			get
+			{
+				return (double)base.WorldMin;
+			}
+			set
+			{
+				if (value > 0.0f)
+				{
+					base.WorldMin = value;
+				}
+				else
+				{
+					throw new NPlotException("Cannot have negative values in Log Axis");
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// The maximum world extent of the axis. Must be greater than zero.
+		/// </summary>
+		public override double WorldMax
+		{
+			get
+			{
+				return (double)base.WorldMax;
+			}
+			set
+			{
+				if (value > 0.0F)
+				{
+					base.WorldMax = value;
+				}
+				else
+				{
+					throw new NPlotException("Cannot have negative values in Log Axis");
+				}
+			}
+		}
+
+		/// <summary>
+		/// Get whether or not this axis is linear. It is not.
+		/// </summary>
+		public override bool IsLinear
+		{
+			get
+			{
+				return false;
+			}
+		}
+
+	}
+}
diff --git a/nplot/nplot/Makefile.am b/nplot/nplot/Makefile.am
new file mode 100644
index 0000000..70b585c
--- /dev/null
+++ b/nplot/nplot/Makefile.am
@@ -0,0 +1,98 @@
+EXTRA_DIST = StrongName.snk
+
+ASSEMBLY_COMPILER_COMMAND = $(GMCS)
+ASSEMBLY_COMPILER_FLAGS =  -debug+
+ASSEMBLY = bin/NPlot.dll
+ASSEMBLY_MDB = $(ASSEMBLY).mdb
+COMPILE_TARGET = library
+BUILD_DIR = bin/
+
+RESGEN=resgen2
+
+SERVER=server
+	
+#all: $(ASSEMBLY) $(SERVER)
+all: $(ASSEMBLY)
+
+
+FILES = \
+	AdapterUtils.cs			\
+	NPlotException.cs 		\
+	ArrowItem.cs			\
+	AssemblyInfo.cs			\
+	AxesConstraint.cs		\
+	Axis.cs				\
+	BasePlot.cs			\
+	BaseSequenceLinePlot.cs		\
+	BaseSequencePlot.cs		\
+	Bitmap.PlotSurface2D.cs		\
+	CandlePlot.cs			\
+	DateTimeAxis.cs			\
+	ErrorHandler.cs			\
+	FilledRegion.cs			\
+	FontScaler.cs			\
+	Grid.cs				\
+	HistogramPlot.cs		\
+	HorizontalLine.cs		\
+	IDrawable.cs			\
+	IGradient.cs			\
+	ImagePlot.cs			\
+	IMeshPlot.cs			\
+	IPlot.cs			\
+	IPlotSurface2D.cs		\
+	ITransform2D.cs			\
+	ISequencePlot.cs		\
+	LabelAxis.cs			\
+	LabelPointPlot.cs		\
+	LegendBase.cs			\
+	Legend.cs			\
+	LinearAxis.cs			\
+	LinearGradient.cs		\
+	LinePlot.cs			\
+	LogAxis.cs			\
+	Marker.cs			\
+	MarkerItem.cs			\
+	PageAlignedPhysicalAxis.cs	\
+	PhysicalAxis.cs			\
+	PiAxis.cs			\
+	PiePlot.cs			\
+	PlotSurface2D.cs		\
+	PlotSurface3D.cs		\
+	PointD.cs			\
+	PointPlot.cs			\
+	RectangleBrushes.cs		\
+	RectangleD.cs			\
+	SequenceAdapter.cs		\
+	StartStep.cs			\
+	StepPlot.cs			\
+	Transform2D.cs			\
+	Utils.cs			\
+	VerticalLine.cs			\
+	Web.Design.PlotSurface2D.cs	\
+	Web.PlotSurface2D.cs
+	
+
+
+REFERENCES =  \
+	System.Web \
+	System.Design \
+	System.Drawing \
+	System.Data
+	
+DLL_REFERENCES = 
+
+CLEANFILES = $(PROGRAMFILES) 
+
+include $(top_srcdir)/Makefile.include
+
+$(eval $(call emit_resgen_targets))
+$(build_xamlg_list): %.xaml.g.cs: %.xaml
+	xamlg '$<'
+
+$(ASSEMBLY) $(ASSEMBLY_MDB): $(build_sources) $(build_resources) $(build_datafiles) $(DLL_REFERENCES) $(PROJECT_REFERENCES) $(build_xamlg_list) $(build_satellite_assembly_list)
+	mkdir -p $(shell dirname $(ASSEMBLY))
+	$(ASSEMBLY_COMPILER_COMMAND) $(ASSEMBLY_COMPILER_FLAGS) -out:$(ASSEMBLY) -target:$(COMPILE_TARGET) $(build_sources_embed) $(build_resources_embed) $(build_references_ref)
+
+
+
+
diff --git a/nplot/nplot/Marker.cs b/nplot/nplot/Marker.cs
new file mode 100644
index 0000000..207a8dc
--- /dev/null
+++ b/nplot/nplot/Marker.cs
@@ -0,0 +1,487 @@
+/*
+NPlot - A charting library for .NET
+
+Marker.cs
+Copyright (C) 2003
+Paolo Pierini, Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Encapsulates functionality relating to markers used by the PointPlot class.
+	/// </summary>
+	public class Marker
+	{
+
+		/// <summary>
+		/// Enumeration of all different types of marker.
+		/// </summary>
+		public enum MarkerType
+		{
+			/// <summary>
+			/// A simple cross marker (x).
+			/// </summary>
+			Cross1,
+			/// <summary>
+			/// Another simple cross marker (+).
+			/// </summary>
+			Cross2,
+			/// <summary>
+			/// A circle marker.
+			/// </summary>
+			Circle,
+			/// <summary>
+			/// A square marker.
+			/// </summary>
+			Square,
+			/// <summary>
+			/// A triangle marker (upwards).
+			/// </summary>
+			Triangle,
+			/// <summary>
+			/// A triangle marker (upwards).
+			/// </summary>
+			TriangleUp,
+			/// <summary>
+			/// A triangle marker (upwards).
+			/// </summary>
+			TriangleDown,
+			/// <summary>
+			/// A diamond,
+			/// </summary>
+			Diamond,
+			/// <summary>
+			/// A filled circle
+			/// </summary>
+			FilledCircle,
+			/// <summary>
+			/// A filled square
+			/// </summary>
+			FilledSquare,
+			/// <summary>
+			/// A filled triangle
+			/// </summary>
+			FilledTriangle,
+			/// <summary>
+			/// A small flag (up)
+			/// </summary>
+			Flag,
+			/// <summary>
+			/// A small flag (up)
+			/// </summary>
+			FlagUp,
+			/// <summary>
+			/// A small flag (down)
+			/// </summary>
+			FlagDown,
+			/// <summary>
+			/// No marker
+			/// </summary>
+			None
+		}
+
+		private MarkerType markerType_;
+		private int size_;
+		private int h_;
+		private System.Drawing.Pen pen_ = new Pen( Color.Black );
+		private System.Drawing.Brush brush_ = new SolidBrush( Color.Black );
+		private bool filled_ = false;
+		private bool dropLine_ = false;
+
+
+		/// <summary>
+		/// The type of marker.
+		/// </summary>
+		public MarkerType Type
+		{
+			get
+			{
+				return markerType_;
+			}
+			set
+			{
+				markerType_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Whether or not to draw a dropline.
+		/// </summary>
+		public bool DropLine
+		{
+			get
+			{
+				return dropLine_;
+			}
+			set
+			{
+				dropLine_ = value;
+			}
+		}
+
+
+        /// <summary>
+		/// The marker size.
+		/// </summary>
+		public int Size
+		{
+			get
+			{
+				return size_;
+			}
+			set
+			{
+				size_ = value;
+				h_ = size_/2;
+			}
+		}
+
+
+		/// <summary>
+		/// The brush used to fill the marker.
+		/// </summary>
+		public Brush FillBrush
+		{
+			get
+			{
+				return brush_;
+			}
+			set
+			{
+				brush_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Fill with color.
+		/// </summary>
+		public bool Filled
+		{
+			get
+			{
+				return filled_;
+			}
+			set
+			{
+				filled_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Sets the pen color and fill brush to be solid with the specified color.
+		/// </summary>
+		public System.Drawing.Color Color
+		{
+			set
+			{
+				pen_.Color = value;
+				brush_ = new SolidBrush( value );
+			}
+			get
+			{
+				return pen_.Color;
+			}
+		}
+
+
+		/// <summary>
+		/// The Pen used to draw the marker.
+		/// </summary>
+		public System.Drawing.Pen Pen
+		{
+			set
+			{
+				pen_ = value;
+			}
+			get
+			{
+				return pen_;
+			}
+		}
+
+
+		/// <summary>
+		/// Default constructor.
+		/// </summary>
+		public Marker()
+		{
+			markerType_ = MarkerType.Square;
+			Size = 4;
+			filled_ = false;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="markertype">The marker type.</param>
+		public Marker( MarkerType markertype )
+		{
+			markerType_ = markertype;
+			Size = 4;
+			filled_ = false;
+		}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="markertype">The marker type.</param>
+		/// <param name="size">The marker size.</param>
+		public Marker( MarkerType markertype, int size )
+		{
+			markerType_ = markertype;
+			Size = size;
+			filled_ = false;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="markertype">The marker type.</param>
+		/// <param name="size">The marker size.</param>
+		/// <param name="color">The marker color.</param>
+		public Marker( MarkerType markertype, int size, Color color )
+		{
+			markerType_ = markertype;
+			Size = size;
+			Color = color;
+			filled_ = false;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="markertype">The marker type.</param>
+		/// <param name="size">The marker size.</param>
+		/// <param name="pen">The marker Pen.</param>
+		public Marker( MarkerType markertype, int size, Pen pen )
+		{
+			markerType_ = markertype;
+			Size = size;
+			Pen = pen;
+			filled_ = false;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="markertype">The marker type.</param>
+		/// <param name="size">The marker size.</param>
+		/// <param name="pen">The marker Pen.</param>
+		/// <param name="fill">The fill flag.</param>
+		public Marker( MarkerType markertype, int size, Pen pen, bool fill )
+		{
+			markerType_ = markertype;
+			Size = size;
+			Pen = pen;
+			filled_ = fill;
+		}
+
+
+
+		/// <summary>
+		/// Draws the marker at the given position
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="x">The [physical] x position to draw the marker.</param>
+		/// <param name="y">The [physical] y position to draw the marker.</param>
+		public void Draw( Graphics g, int x, int y )
+		{
+
+			switch (markerType_)
+			{
+
+				case MarkerType.Cross1:
+					g.DrawLine( pen_, x-h_, y+h_, x+h_, y-h_ );
+					g.DrawLine( pen_, x+h_, y+h_, x-h_, y-h_ );
+					break;
+
+				case MarkerType.Cross2:
+					g.DrawLine( pen_, x, y-h_, x, y+h_ );
+					g.DrawLine( pen_, x-h_, y, x+h_, y );
+					break;
+
+				case MarkerType.Circle:
+					g.DrawEllipse( pen_, x-h_, y-h_, size_, size_ );
+					if ( this.filled_ ) 
+					{
+						g.FillEllipse( brush_, x-h_, y-h_, size_, size_ );
+					}
+					break;
+
+				case MarkerType.Square:
+					g.DrawRectangle( pen_, x-h_, y-h_, size_, size_ );
+					if ( this.filled_ ) 
+					{
+						g.FillRectangle( brush_, x-h_, y-h_, size_, size_ );
+					}
+					break;
+
+				case MarkerType.Triangle:
+				case MarkerType.TriangleDown:
+				{
+					Point p1 = new Point( x-h_, y-h_ );
+					Point p2 = new Point( x, y+h_ );
+					Point p3 = new Point( x+h_, y-h_ );
+					Point [] pts = new Point [3] { p1, p2, p3 };
+					GraphicsPath gp = new GraphicsPath();
+					gp.AddPolygon( pts );
+					g.DrawPath( pen_, gp );
+					if (this.filled_)
+					{
+						g.FillPath( brush_, gp );
+					}
+					break;
+				}
+				case MarkerType.TriangleUp:
+				{
+					Point p1 = new Point( x-h_, y+h_ );
+					Point p2 = new Point( x, y-h_ );
+					Point p3 = new Point( x+h_, y+h_ );
+					Point [] pts = new Point [3] { p1, p2, p3 };
+					GraphicsPath gp = new GraphicsPath();
+					gp.AddPolygon( pts );
+					g.DrawPath( pen_, gp );
+					if (this.filled_) 
+					{
+						g.FillPath( brush_, gp );
+					}
+					break;
+				}
+				case MarkerType.FilledCircle:
+					g.DrawEllipse( pen_, x-h_, y-h_, size_, size_ );
+					g.FillEllipse( brush_, x-h_, y-h_, size_, size_ );
+					break;
+
+				case MarkerType.FilledSquare:
+					g.DrawRectangle( pen_, x-h_, y-h_, size_, size_ );
+					g.FillRectangle( brush_, x-h_, y-h_, size_, size_ );
+					break;
+
+				case MarkerType.FilledTriangle:
+				{
+					Point p1 = new Point( x-h_, y-h_ );
+					Point p2 = new Point( x, y+h_ );
+					Point p3 = new Point( x+h_, y-h_ );
+					Point [] pts = new Point [3] { p1, p2, p3 };
+					GraphicsPath gp = new GraphicsPath();
+					gp.AddPolygon( pts );
+					g.DrawPath( pen_, gp );
+					g.FillPath( brush_, gp );
+					break;
+				}
+				case MarkerType.Diamond:
+				{
+					Point p1 = new Point( x-h_, y );
+					Point p2 = new Point( x, y-h_ );
+					Point p3 = new Point( x+h_, y );
+					Point p4 = new Point( x, y+h_ );
+					Point [] pts = new Point [4] { p1, p2, p3, p4 };
+					GraphicsPath gp = new GraphicsPath();
+					gp.AddPolygon( pts );
+					g.DrawPath( pen_, gp );
+					if (this.filled_)
+					{
+						g.FillPath( brush_, gp );
+					}
+					break;
+				}
+				case MarkerType.Flag:
+				case MarkerType.FlagUp:
+				{
+					Point p1 = new Point( x, y );
+					Point p2 = new Point( x, y-size_ );
+					Point p3 = new Point( x+size_, y-size_+size_/3 );
+					Point p4 = new Point( x, y-size_+2*size_/3 );
+					g.DrawLine( pen_, p1, p2 );
+					Point [] pts = new Point [3] { p2, p3, p4 };
+					GraphicsPath gp = new GraphicsPath();
+					gp.AddPolygon( pts );
+					g.DrawPath( pen_, gp );
+					if (this.filled_)
+					{
+						g.FillPath( brush_, gp );
+					}
+					break;
+				}
+				case MarkerType.FlagDown:
+				{
+					Point p1 = new Point( x, y );
+					Point p2 = new Point( x, y+size_ );
+					Point p3 = new Point( x+size_, y+size_-size_/3 );
+					Point p4 = new Point( x, y+size_-2*size_/3 );
+					g.DrawLine( pen_, p1, p2 );
+					Point [] pts = new Point [3] { p2, p3, p4 };
+					GraphicsPath gp = new GraphicsPath();
+					gp.AddPolygon( pts );
+					g.DrawPath( pen_, gp );
+					if (this.filled_)
+					{
+						g.FillPath( brush_, gp );
+					}
+					break;
+				}
+				case MarkerType.None:
+					break;
+			}
+
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/MarkerItem.cs b/nplot/nplot/MarkerItem.cs
new file mode 100644
index 0000000..ac957c9
--- /dev/null
+++ b/nplot/nplot/MarkerItem.cs
@@ -0,0 +1,136 @@
+/*
+NPlot - A charting library for .NET
+
+MarkerItem.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// Class for placement of a single marker.
+	/// </summary>
+	public class MarkerItem : IDrawable
+	{
+
+		private Marker marker_; 
+		private double x_;
+		private double y_; 
+
+
+		/// <summary>
+		/// Constructs a square marker at the (world) point point.
+		/// </summary>
+		/// <param name="point">the world position at which to place the marker</param>
+		public MarkerItem( PointD point )
+		{
+			marker_ = new Marker( Marker.MarkerType.Square );
+			x_ = point.X;
+			y_ = point.Y;
+		}
+
+
+		/// <summary>
+		/// Default constructor - a square black marker.
+		/// </summary>
+		/// <param name="x">The world x position of the marker</param>
+		/// <param name="y">The world y position of the marker</param>
+		public MarkerItem( double x, double y )
+		{
+			marker_ = new Marker( Marker.MarkerType.Square );
+			x_ = x;
+			y_ = y;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="marker">The marker to place on the chart.</param>
+		/// <param name="x">The world x position of the marker</param>
+		/// <param name="y">The world y position of the marker</param>
+		public MarkerItem( Marker marker, double x, double y )
+		{
+			marker_ = marker;
+			x_ = x;
+			y_ = y;
+		}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="marker">The marker to place on the chart.</param>
+		/// <param name="point">The world position of the marker</param>
+		public MarkerItem( Marker marker, PointD point )
+		{
+			marker_ = marker;
+			x_ = point.X;
+			y_ = point.Y;
+		}
+
+		/// <summary>
+		/// Draws the marker on a plot surface.
+		/// </summary>
+		/// <param name="g">graphics surface on which to draw</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			PointF point = new PointF( 
+				xAxis.WorldToPhysical( x_, true ).X,
+				yAxis.WorldToPhysical( y_, true ).Y );
+
+			marker_.Draw( g, (int)point.X, (int)point.Y );
+		}
+	
+
+	}
+}
diff --git a/nplot/nplot/NPlotException.cs b/nplot/nplot/NPlotException.cs
new file mode 100644
index 0000000..4d032df
--- /dev/null
+++ b/nplot/nplot/NPlotException.cs
@@ -0,0 +1,90 @@
+/*
+NPlot - A charting library for .NET
+
+NPlotException.cs
+Copyright (C) 2005
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// All exceptions thrown by NPlot are of this type.
+	/// </summary>
+	public class NPlotException : System.Exception
+	{
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="message">The error message that explains the reason for the exception.</param>
+		/// <param name="innerException">The exception that is the cause of the current exception.</param>
+		public NPlotException( string message, System.Exception innerException )
+			: base( message, innerException )
+		{
+		}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="message">The error message that explains the reason for the exception.</param>
+		public NPlotException( string message )
+			: base( message )
+		{
+		}
+
+		/// <summary>
+		/// Constructor.
+		/// </summary>
+		public NPlotException()
+		{
+		}
+
+	}
+}
diff --git a/nplot/nplot/PageAlignedPhysicalAxis.cs b/nplot/nplot/PageAlignedPhysicalAxis.cs
new file mode 100644
index 0000000..2dd9772
--- /dev/null
+++ b/nplot/nplot/PageAlignedPhysicalAxis.cs
@@ -0,0 +1,151 @@
+/*
+NPlot - A charting library for .NET
+
+PageAlignedPhysicalAxis.cs
+Copyright (C) 2005
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// The bare minimum needed to do world->physical and physical->world transforms for
+	/// vertical axes. Also includes tick placements. Built for speed.
+	/// </summary>
+	/// <remarks>currently unused</remarks>
+	public class PageAlignedPhysicalAxis
+	{
+
+		private int pMin_;
+		private int pMax_;
+		private int pLength_;      // cached.
+
+		private double worldMin_;
+		private double worldMax_;
+		private double worldLength_; // cached.
+		
+
+		/// <summary>
+		/// Construct from a fully-blown physical axis.
+		/// </summary>
+		/// <param name="physicalAxis">the physical axis to get initial values from.</param>
+		public PageAlignedPhysicalAxis( PhysicalAxis physicalAxis )
+		{
+			worldMin_ = physicalAxis.Axis.WorldMin;
+			worldMax_ = physicalAxis.Axis.WorldMax;
+			worldLength_ = worldMax_ - worldMin_;
+
+			if ( physicalAxis.PhysicalMin.X == physicalAxis.PhysicalMax.X )
+			{
+				pMin_ = physicalAxis.PhysicalMin.Y;
+				pMax_ = physicalAxis.PhysicalMax.Y;
+			}
+			else if ( physicalAxis.PhysicalMin.Y == physicalAxis.PhysicalMax.Y )
+			{
+				pMin_ = physicalAxis.PhysicalMin.X;
+				pMax_ = physicalAxis.PhysicalMax.X;
+			}
+			else
+			{
+				throw new NPlotException( "Physical axis is not page aligned" );
+			}
+
+			pLength_ = pMax_ - pMin_;
+
+		}
+
+		
+		/// <summary>
+		/// return the physical coordinate corresponding to the supplied world coordinate.
+		/// </summary>
+		/// <param name="world">world coordinate to determine physical coordinate for.</param>
+		/// <returns>the physical coordinate corresoindng to the supplied world coordinate.</returns>
+		public float WorldToPhysical( double world )
+		{
+			return (float)(((world-worldMin_) / worldLength_) * (float)pLength_ + (float)pMin_);
+		}
+
+
+		/// <summary>
+		/// return the physical coordinate corresponding to the supplied world coordinate,
+		/// clipped if it is outside the bounds of the axis
+		/// </summary>
+		/// <param name="world">world coordinate to determine physical coordinate for.</param>
+		/// <returns>the physical coordinate corresoindng to the supplied world coordinate.</returns>
+		public float WorldToPhysicalClipped( double world )
+		{
+			if (world > worldMax_)
+			{
+				return pMax_;
+			}
+			
+			if (world < worldMin_)
+			{
+				return pMin_;
+			}
+			
+			// is this quicker than returning WorldToPhysical?
+			return (float)(((world-worldMin_) / worldLength_) * (float)pLength_ + (float)pMin_);
+		}
+	
+
+		/// <summary>
+		/// return the world coordinate corresponding to the supplied physical coordinate.
+		/// </summary>
+		/// <param name="physical">physical coordinate to determine world coordinate for.</param>
+		/// <returns>the world coordinate corresponding to the supplied </returns>
+		public double PhysicalToWorld( float physical )
+		{
+			return ((float)(physical-pMin_) / (float)pLength_) * worldLength_ + worldMin_;
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/PhysicalAxis.cs b/nplot/nplot/PhysicalAxis.cs
new file mode 100644
index 0000000..3b323d6
--- /dev/null
+++ b/nplot/nplot/PhysicalAxis.cs
@@ -0,0 +1,276 @@
+/*
+NPlot - A charting library for .NET
+
+PhysicalAxis.cs
+Copyright (C) 2003
+Matt Howlett, Paolo Pierini
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+using System.Collections;
+
+namespace NPlot
+{
+	/// <summary>
+	/// This class adds physical positioning information [PhysicalMin, PhysicalMax]
+	/// and related functionality on top of a specific Axis class. 
+	/// 
+	/// It's an interesting
+	/// question where to put this information. It belongs with every specific axis
+	/// type, but on the other hand, users of the library as it is normally used 
+	/// should not see it because
+	/// positioning of axes is handled internally by PlotSurface2D. Therefore it doesn't make sense
+	/// to put it in the Axis class unless it is internal. But if this were done it would restrict
+	/// use of this information outside the library always, which is not what is wanted.
+	/// The main disadvantage with the method chosen is that there is a lot of passing
+	/// of the positional information between physical axis and the underlying logical
+	/// axis type.
+	/// 
+	///	C# doesn't have templates. If it did, I might derive PhysicalAxis from the 
+	/// templated Axis type (LinearAxis etc). Instead, have used a has-a relationship
+	/// with an Axis superclass.
+	/// </summary>
+	public class PhysicalAxis
+	{
+
+		/// <summary>
+		/// Prevent default construction.
+		/// </summary>
+		private PhysicalAxis()
+		{
+		}
+
+
+		/// <summary>
+		/// Construct
+		/// </summary>
+		/// <param name="a">The axis this is a physical representation of.</param>
+		/// <param name="physicalMin">the physical position of the world minimum axis value.</param>
+		/// <param name="physicalMax">the physical position of the world maximum axis value.</param>
+		public PhysicalAxis( Axis a, Point physicalMin, Point physicalMax )
+		{
+			this.Axis = a;
+			this.PhysicalMin = physicalMin;
+			this.PhysicalMax = physicalMax;
+		}
+
+
+		/// <summary>
+		/// Returns the smallest rectangle that completely contains all parts of the axis [including ticks and label].
+		/// </summary>
+		/// <returns>the smallest rectangle that completely contains all parts of the axis [including ticks and label].</returns>
+		public virtual Rectangle GetBoundingBox()
+		{
+			System.Drawing.Bitmap scratchArea_ = new System.Drawing.Bitmap( 1, 1 );
+			Graphics g = Graphics.FromImage( scratchArea_ );
+			Rectangle bounds;
+			this.Draw( g, out bounds );
+			return bounds;
+		}
+
+	
+		/// <summary>
+		/// Draws the axis on the given graphics surface.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="boundingBox">out: the axis bounding box - the smallest rectangle that
+		/// completely contains all parts of the axis [including ticks and label].</param>
+		public virtual void Draw( System.Drawing.Graphics g, out Rectangle boundingBox )
+		{
+			this.Axis.Draw( g, PhysicalMin, PhysicalMax, out boundingBox );
+		}
+
+
+		/// <summary>
+		/// Given a world coordinate value, returns the physical position of the 
+		/// coordinate along the axis.
+		/// </summary>
+		/// <param name="coord">the world coordinate</param>
+		/// <param name="clip">if true, the physical position returned will be clipped to the physical max / min position as appropriate if the world value is outside the limits of the axis.</param>
+		/// <returns>the physical position of the coordinate along the axis.</returns>
+		public PointF WorldToPhysical( double coord, bool clip )
+		{
+			return Axis.WorldToPhysical( coord, PhysicalMin, PhysicalMax, clip );
+		}
+
+
+		/// <summary>
+		/// Given a physical point on the graphics surface, returns the world
+		/// value of it's projection onto the axis [i.e. closest point on the axis]. 
+		/// The function is implemented for axes of arbitrary orientation.
+		/// </summary>
+		/// <param name="p">Physical point to find corresponding world value of.</param>
+		/// <param name="clip">if true, returns a world position outside WorldMin / WorldMax
+		/// range if this is closer to the axis line. If false, such values will
+		/// be clipped to be either WorldMin or WorldMax as appropriate.</param>
+		/// <returns>the world value of the point's projection onto the axis.</returns>
+		public double PhysicalToWorld( Point p, bool clip )
+		{
+			return Axis.PhysicalToWorld( p, PhysicalMin, PhysicalMax, clip );
+		}
+
+
+		/// <summary>
+		/// This sets new world limits for the axis from two physical points
+		/// selected within the plot area.
+		/// </summary>
+		/// <param name="min">The upper left point of the selection.</param>
+		/// <param name="max">The lower right point of the selection.</param>
+		public void SetWorldLimitsFromPhysical( Point min, Point max )
+		{
+			double minc;
+			double maxc;
+			if (Axis != null)
+			{
+				minc = Axis.WorldMin;
+				maxc = Axis.WorldMax;
+				if ( !Axis.Reversed ) 
+				{
+					double tmp = this.PhysicalToWorld(min,true);
+					Axis.WorldMax = this.PhysicalToWorld(max,true);
+					Axis.WorldMin = tmp;
+				}
+				else
+				{
+					double tmp = this.PhysicalToWorld(min,true);
+					Axis.WorldMin = this.PhysicalToWorld(max,true);
+					Axis.WorldMax = tmp;
+				}
+				// need to trap somehow if the user selects an 
+				// arbitrarily small range. Otherwise the GDI+ 
+				// drawing routines lead to an overflow in painting 
+				// the picture. This may be not the optimal solution,
+				// but if the GDI+ draw leads to an overflow the
+				// graphic surface becomes unusable anymore and I
+				// had difficulty to trap the error.
+				double half = (Axis.WorldMin + Axis.WorldMax)/2;
+				double width = Axis.WorldMax - Axis.WorldMin;
+				if (Math.Abs(half/width) > 1.0e12)
+				{
+					Axis.WorldMin = minc;
+					Axis.WorldMax = maxc;
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// The physical position corresponding to WorldMin.
+		/// </summary>
+		public Point PhysicalMin
+		{
+			get
+			{
+				return physicalMin_;
+			}
+			set
+			{
+				physicalMin_ = value;
+			}
+		}
+		private Point physicalMin_;
+
+
+		/// <summary>
+		/// The physical position corresponding to WorldMax.
+		/// </summary>
+		public Point PhysicalMax
+		{
+			get
+			{
+				return physicalMax_;
+			}
+			set
+			{
+				physicalMax_ = value;
+			}
+		}
+		private Point physicalMax_;
+
+
+		/// <summary>
+		/// The axis this object adds physical extents to.
+		/// </summary>
+		public Axis Axis
+		{
+			get
+			{
+				return axis_;
+			}
+			set
+			{
+				axis_ = value;
+			}
+		}
+		private Axis axis_;
+	
+
+		/// <summary>
+		/// The length in pixels of the axis.
+		/// </summary>
+		public int PhysicalLength
+		{
+			get
+			{
+				return Utils.Distance( PhysicalMin, PhysicalMax );
+			}
+		}
+
+		/// <summary>
+		/// The length in world coordinates of one pixel. 
+		/// </summary>
+		public double PixelWorldLength
+		{
+			get
+			{
+				return this.Axis.WorldLength / this.PhysicalLength;
+			}
+		}
+
+	}
+}
diff --git a/nplot/nplot/PiAxis.cs b/nplot/nplot/PiAxis.cs
new file mode 100644
index 0000000..3aa5316
--- /dev/null
+++ b/nplot/nplot/PiAxis.cs
@@ -0,0 +1,230 @@
+/*
+NPlot - A charting library for .NET
+
+PiAxis.cs
+Copyright (C) 2004
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Axis with labels in multiples of Pi. Maybe needs a better name.
+	/// Lots of functionality still to be added - currently only puts labels
+	/// at whole increments of pi, want arbitrary increments, automatically
+	/// determined and dependance on physical length. 
+	/// Volunteers? 
+	/// </summary>
+	public class PiAxis : Axis
+	{
+
+		/// <summary>
+		/// Deep copy of PiAxis.
+		/// </summary>
+		/// <returns>A copy of the LinearAxis Class.</returns>
+		public override object Clone()
+		{
+			PiAxis a = new PiAxis();
+			// ensure that this isn't being called on a derived type. If it is, then oh no!
+			if (this.GetType() != a.GetType())
+			{
+				throw new NPlotException( "Error. Clone method is not defined in derived type." );
+			}
+			DoClone( this, a );
+			return a;
+		}
+
+
+		/// <summary>
+		/// Helper method for Clone.
+		/// </summary>
+		/// <param name="a">The original object to clone.</param>
+		/// <param name="b">The cloned object.</param>
+		protected static void DoClone( PiAxis b, PiAxis a )
+		{
+			Axis.DoClone( b, a );
+		}
+
+
+		/// <summary>
+		/// Initialise PiAxis to default state.
+		/// </summary>
+		private void Init()
+		{
+		}
+
+
+		/// <summary>
+		/// Copy constructor
+		/// </summary>
+		/// <param name="a">The Axis to clone.</param>
+		/// <remarks>TODO: [review notes] I don't think this will work as desired.</remarks>
+		public PiAxis( Axis a )
+			: base( a )
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Default constructor
+		/// </summary>
+		public PiAxis()
+			: base()
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="worldMin">Minimum world value</param>
+		/// <param name="worldMax">Maximum world value</param>
+		public PiAxis( double worldMin, double worldMax )
+			: base( worldMin, worldMax )
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Given Graphics surface, and physical extents of axis, draw ticks and
+		/// associated labels.
+		/// </summary>
+		/// <param name="g">The GDI+ Graphics surface on which to draw.</param>
+		/// <param name="physicalMin">The physical location of the world min point</param>
+		/// <param name="physicalMax">The physical location of the world max point</param>
+		/// <param name="boundingBox">out: smallest box that completely encompasses all of the ticks and tick labels.</param>
+		/// <param name="labelOffset">out: a suitable offset from the axis to draw the axis label.</param>
+		protected override void DrawTicks( 
+			Graphics g, 
+			Point physicalMin, 
+			Point physicalMax, 
+			out object labelOffset,
+			out object boundingBox )
+		{
+			Point tLabelOffset;
+			Rectangle tBoundingBox;
+
+			labelOffset = this.getDefaultLabelOffset( physicalMin, physicalMax );
+			boundingBox = null;
+
+			int start = (int)Math.Ceiling( this.WorldMin / Math.PI );
+			int end = (int)Math.Floor( this.WorldMax / Math.PI );
+			
+			// sanity checking.
+			if ( end - start < 0 || end - start > 30 )
+			{
+				return;
+			}
+
+			for (int i=start; i<=end; ++i)
+			{
+				string label = i.ToString() + "Pi";
+
+				if (i == 0)
+					label = "0";
+				else if (i == 1)
+					label = "Pi";
+
+				this.DrawTick( g, i*Math.PI, this.LargeTickSize, 
+					label,
+					new Point(0,0), 
+					physicalMin, physicalMax,
+					out tLabelOffset, out tBoundingBox );
+
+				Axis.UpdateOffsetAndBounds( 
+					ref labelOffset, ref boundingBox, 
+					tLabelOffset, tBoundingBox );
+			}
+
+		}
+
+
+		/// <summary>
+		/// Determines the positions, in world coordinates, of the large ticks. 
+		/// 
+		/// Label axes do not have small ticks.
+		/// </summary>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">null</param>
+		internal override void WorldTickPositions_FirstPass(
+			Point physicalMin, 
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+			smallTickPositions = null;
+			largeTickPositions = new ArrayList();
+	
+			int start = (int)Math.Ceiling( this.WorldMin / Math.PI );
+			int end = (int)Math.Floor( this.WorldMax / Math.PI );
+
+			// sanity checking.
+			if ( end - start < 0 || end - start > 30 )
+			{
+				return;
+			}
+
+			for (int i=start; i<end; ++i)
+			{
+				largeTickPositions.Add( i*Math.PI ); 
+			}
+
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/PiePlot.cs b/nplot/nplot/PiePlot.cs
new file mode 100644
index 0000000..4d0b509
--- /dev/null
+++ b/nplot/nplot/PiePlot.cs
@@ -0,0 +1,120 @@
+/*
+NPlot - A charting library for .NET
+
+PiePlot.cs
+Copyright (C) 2004
+Thierry Malo
+
+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
+
+$Id: PiePlot.cs,v 1.3 2004/10/23 07:08:35 mhowlett Exp $
+
+*/
+
+/*
+using System;
+using System.Drawing;
+
+namespace scpl
+{
+	/// <summary>
+	/// Description résumée de PiePlot.
+	/// </summary>
+	public class PiePlot : BasePlot, IPlot
+	{
+		public PiePlot(ISequenceAdapter datas)
+		{
+			//
+			// TODO : ajoutez ici la logique du constructeur
+			//
+			this.Data=datas;
+
+			_brushes=new Brush[_MaxBrush];
+
+        _brushes[0] = Brushes.DarkBlue;
+        _brushes[1] = Brushes.Yellow;
+        _brushes[2] = Brushes.Green;
+        _brushes[3] = Brushes.Brown;
+        _brushes[4] = Brushes.Blue;
+        _brushes[5] = Brushes.Red;
+        _brushes[6] = Brushes.LightGreen;
+        _brushes[7] = Brushes.Salmon;
+}
+
+		private ISequenceAdapter data_;
+		private double _Total;
+		const int _MaxBrush=8;
+		private Brush[] _brushes;
+
+		public ISequenceAdapter Data
+		{
+			get
+			{
+				return data_;
+			}
+			set
+			{
+				data_ = value;
+
+				// calculate the sum of all value (this is related to 360°)
+				_Total = 0;
+				for ( int i=0; i<data_.Count; ++i )
+				{
+					_Total += data_[i].Y;
+				}
+			}
+		}
+
+		#region SuggestXAxis
+		public virtual Axis SuggestXAxis()
+		{
+				return data_.SuggestXAxis();
+		}
+		#endregion
+
+		#region SuggestXAxis
+		public virtual Axis SuggestYAxis()
+		{
+			return data_.SuggestYAxis();
+		}
+		#endregion
+
+		public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			int LastAngle=0,ReelAngle;
+
+			float rx=(xAxis.PhysicalMax.X - xAxis.PhysicalMin.X);
+			float ry=(yAxis.PhysicalMin.Y - yAxis.PhysicalMax.Y);
+
+			int h=(int) (ry * 0.8);
+			int w= (int) (rx * 0.8);
+
+			// This is to keep the pie based on a circle (i.e. inside a square)
+			int s=Math.Min(h,w);
+
+			// calculate boundary rectangle coordinate
+			int cy=(int) (yAxis.PhysicalMax.Y + (h * 1.2 - s) / 2);
+			int cx=(int) (xAxis.PhysicalMin.X + (w * 1.2 - s) / 2);
+
+			for (int i=0; i<this.Data.Count; ++i)
+			{
+				ReelAngle = (int) (data_[i].Y * 360 / _Total);
+				g.FillPie(_brushes[i % _MaxBrush], cx, cy, s, s, LastAngle, ReelAngle);
+				LastAngle += ReelAngle;
+			}
+		}
+	}
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/PlotSurface2D.cs b/nplot/nplot/PlotSurface2D.cs
new file mode 100644
index 0000000..d0bca8c
--- /dev/null
+++ b/nplot/nplot/PlotSurface2D.cs
@@ -0,0 +1,1334 @@
+/*
+NPlot - A charting library for .NET
+
+PlotSurface2D.cs
+Copyright (C) 2003
+Matt Howlett, Paolo Pierini
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+*/
+
+// #define DEBUG_BOUNDING_BOXES
+
+using System;
+using System.Drawing;
+using System.Diagnostics;
+using System.Collections;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Implements the surface on which IDrawables are drawn. Is extended
+	/// by Bitmap.PlotSurface2D, Windows.PlotSurface2D etc. TODO: better explanation.
+	/// </summary>
+	public class PlotSurface2D : IPlotSurface2D
+	{
+
+		/// <summary>
+		/// Possible positions of the X axis.
+		/// </summary>
+		public enum XAxisPosition
+		{
+			/// <summary>
+			/// X axis is on the top.
+			/// </summary>
+			Top = 1,
+			//Center = 2,
+			/// <summary>
+			/// X axis is on the bottom.
+			/// </summary>
+			Bottom = 3,
+		}
+
+
+		/// <summary>
+		/// Possible positions of the Y axis.
+		/// </summary>
+		public enum YAxisPosition
+		{
+			/// <summary>
+			/// Y axis on the left.
+			/// </summary>
+			Left = 1,
+			// Center
+			/// <summary>
+			/// Y axis on the right.
+			/// </summary>
+			Right = 3,
+		}
+
+
+		private System.Drawing.StringFormat titleDrawFormat_;
+
+		private Font titleFont_;
+		private string title_;
+		private Brush titleBrush_;
+		private int padding_;
+		private Axis xAxis1_;
+		private Axis yAxis1_;
+		private Axis xAxis2_;
+		private Axis yAxis2_;
+		private PhysicalAxis pXAxis1Cache_;
+		private PhysicalAxis pYAxis1Cache_;
+		private PhysicalAxis pXAxis2Cache_;
+		private PhysicalAxis pYAxis2Cache_;
+		private bool autoScaleAutoGeneratedAxes_ = false;
+		private bool autoScaleTitle_ = false;
+
+		private object plotAreaBoundingBoxCache_;
+		private object bbXAxis1Cache_;
+		private object bbXAxis2Cache_;
+		private object bbYAxis1Cache_;
+		private object bbYAxis2Cache_;
+		private object bbTitleCache_;
+
+		private object plotBackColor_ = null;
+		private System.Drawing.Bitmap plotBackImage_ = null;
+		private IRectangleBrush plotBackBrush_ = null;
+
+		private System.Collections.ArrayList drawables_;
+		private System.Collections.ArrayList xAxisPositions_;
+		private System.Collections.ArrayList yAxisPositions_;
+        private System.Collections.ArrayList zPositions_;
+        private System.Collections.SortedList ordering_;
+
+        private System.Drawing.Drawing2D.SmoothingMode smoothingMode_;
+
+		private ArrayList axesConstraints_ = null;
+
+		private Legend legend_;
+
+
+		/// <summary>
+		/// The physical bounding box of the last drawn plot surface area is available here.
+		/// </summary>
+		public Rectangle PlotAreaBoundingBoxCache
+		{
+			get
+			{
+				if (plotAreaBoundingBoxCache_ == null) 
+				{
+					return Rectangle.Empty;
+				}
+				else
+				{
+					return (Rectangle)plotAreaBoundingBoxCache_;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Performs a hit test with the given point and returns information 
+		/// about the object being hit.
+		/// </summary>
+		/// <param name="p">The point to test.</param>
+		/// <returns></returns>
+		public System.Collections.ArrayList HitTest(Point p)
+		{
+
+			System.Collections.ArrayList a = new System.Collections.ArrayList();
+
+			// this is the case if PlotSurface has been cleared.
+			if (bbXAxis1Cache_ == null)
+			{
+				return a;
+			}
+			else if (bbXAxis1Cache_ != null && ((Rectangle) bbXAxis1Cache_ ).Contains(p))
+			{
+				a.Add( this.xAxis1_ );
+				return a;
+			}
+			else if (bbYAxis1Cache_ != null && ((Rectangle) bbYAxis1Cache_ ).Contains(p))
+			{
+				a.Add( this.yAxis1_ );
+				return a;
+			}
+			else if (bbXAxis2Cache_ != null && ((Rectangle) bbXAxis2Cache_ ).Contains(p))
+			{
+				a.Add( this.xAxis2_ );
+				return a;
+			}
+			else if (bbXAxis2Cache_ != null && ((Rectangle) bbYAxis2Cache_ ).Contains(p))
+			{
+				a.Add( this.yAxis2_ );
+				return a;
+			}
+			else if (bbTitleCache_ != null && ((Rectangle) bbTitleCache_ ).Contains(p))
+			{
+				a.Add( this );
+				return a;
+			}
+			else if (plotAreaBoundingBoxCache_ != null && ((Rectangle) plotAreaBoundingBoxCache_ ).Contains(p))
+			{
+				a.Add( this );
+				return a;
+			}
+
+			return a;
+		}
+
+
+		/// <summary>
+		/// The bottom abscissa axis.
+		/// </summary>
+		public Axis XAxis1
+		{
+			get
+			{
+				return xAxis1_;
+			}
+			set
+			{
+				xAxis1_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The left ordinate axis.
+		/// </summary>
+		public Axis YAxis1
+		{
+			get
+			{
+				return yAxis1_;
+			}
+			set
+			{
+				yAxis1_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The top abscissa axis.
+		/// </summary>
+		public Axis XAxis2
+		{
+			get
+			{
+				return xAxis2_;
+			}
+			set
+			{
+				xAxis2_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The right ordinate axis.
+		/// </summary>
+		public Axis YAxis2
+		{
+			get
+			{
+				return yAxis2_;
+			}
+			set
+			{
+				yAxis2_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical XAxis1 that was last drawn.
+		/// </summary>
+		public PhysicalAxis PhysicalXAxis1Cache
+		{
+			get
+			{
+				return pXAxis1Cache_;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical YAxis1 that was last drawn.
+		/// </summary>
+		public PhysicalAxis PhysicalYAxis1Cache
+		{
+			get
+			{
+				return pYAxis1Cache_;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical XAxis2 that was last drawn.
+		/// </summary>
+		public PhysicalAxis PhysicalXAxis2Cache
+		{
+			get
+			{
+				return pXAxis2Cache_;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical YAxis2 that was last drawn.
+		/// </summary>
+		public PhysicalAxis PhysicalYAxis2Cache
+		{
+			get
+			{
+				return pYAxis2Cache_;
+			}
+		}
+
+
+
+		/// <summary>
+		/// The chart title.
+		/// </summary>
+		public string Title
+		{
+			get
+			{
+				return title_;
+			}
+			set
+			{
+				title_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The plot title font.
+		/// </summary>
+		public Font TitleFont
+		{
+			get
+			{
+				return titleFont_;
+			}
+			set
+			{
+				titleFont_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The distance in pixels to leave between of the edge of the bounding rectangle
+		/// supplied to the Draw method, and the markings that make up the plot.
+		/// </summary>
+		public int Padding
+		{
+			get
+			{
+				return padding_;
+			}
+			set
+			{
+				padding_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Sets the title to be drawn using a solid brush of this color.
+		/// </summary>
+		public Color TitleColor
+		{
+			set
+			{
+				titleBrush_ = new SolidBrush( value );
+			}
+		}
+
+
+		/// <summary>
+		/// The brush used for drawing the title.
+		/// </summary>
+		public Brush TitleBrush
+		{
+			get
+			{
+				return titleBrush_;
+			}
+			set
+			{
+				titleBrush_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// A color used to paint the plot background. Mutually exclusive with PlotBackImage and PlotBackBrush
+		/// </summary>
+		public System.Drawing.Color PlotBackColor
+		{
+			set
+			{
+				plotBackColor_ = value;
+				plotBackBrush_ = null;
+				plotBackImage_ = null;
+			}
+		}
+
+		
+		/// <summary>
+		/// An imaged used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+		/// </summary>
+		public System.Drawing.Bitmap PlotBackImage
+		{
+			set
+			{
+				plotBackImage_ = value;
+				plotBackColor_ = null;
+				plotBackBrush_ = null;
+			}
+		}
+
+
+		/// <summary>
+		/// A Rectangle brush used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+		/// </summary>
+		public IRectangleBrush PlotBackBrush
+		{
+			set
+			{
+				plotBackBrush_ = value;
+				plotBackColor_ = null;
+				plotBackImage_ = null;
+			}
+		}
+
+
+		/// <summary>
+		/// Smoothing mode to use when drawing plots.
+		/// </summary>
+		public System.Drawing.Drawing2D.SmoothingMode SmoothingMode 
+		{ 
+			get
+			{
+				return smoothingMode_;
+			}
+			set
+			{
+				this.smoothingMode_ = value;
+			}
+		}
+
+
+		private void Init()
+		{
+			drawables_ = new ArrayList();
+			xAxisPositions_ = new ArrayList();
+			yAxisPositions_ = new ArrayList();
+            zPositions_ = new ArrayList();
+            ordering_ = new SortedList();
+            FontFamily fontFamily = new FontFamily("Arial");
+			TitleFont = new Font(fontFamily, 14, FontStyle.Regular, GraphicsUnit.Pixel);
+			padding_ = 10;
+			title_ = "";
+			autoScaleTitle_ = false;
+			autoScaleAutoGeneratedAxes_ = false;
+			xAxis1_ = null;
+			xAxis2_ = null;
+			yAxis1_ = null;
+			yAxis2_ = null;
+			pXAxis1Cache_ = null;
+			pYAxis1Cache_ = null;
+			pXAxis2Cache_ = null;
+			pYAxis2Cache_ = null;
+			titleBrush_ = new SolidBrush( Color.Black );
+			plotBackColor_ = Color.White;
+			
+			this.legend_ = null;
+
+			smoothingMode_ = System.Drawing.Drawing2D.SmoothingMode.None;
+
+			axesConstraints_ = new ArrayList();
+		}
+
+
+		/// <summary>
+		/// Default constructor.
+		/// </summary>
+		public PlotSurface2D()
+		{
+			// only create this once.
+			titleDrawFormat_ = new StringFormat();
+			titleDrawFormat_.Alignment = StringAlignment.Center;
+
+			Init();
+		}
+
+
+		private float DetermineScaleFactor( int w, int h )
+		{
+
+			float diag = (float)Math.Sqrt( w*w +  h*h );
+			float scaleFactor = (diag / 1400.0f)*2.4f;
+			
+			if ( scaleFactor > 1.0f )
+			{
+				return scaleFactor;
+			}
+			else
+			{
+				return 1.0f;
+			}
+		}
+
+
+        /// <summary>
+        /// Adds a drawable object to the plot surface with z-order 0. If the object is an IPlot,
+        /// the PlotSurface2D axes will also be updated. 
+        /// </summary>
+        /// <param name="p">The IDrawable object to add to the plot surface.</param>
+        public void Add(IDrawable p)
+        {
+            Add(p, 0);
+        }
+
+
+        /// <summary>
+		/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+		/// the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">The IDrawable object to add to the plot surface.</param>
+		/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+		public void Add( IDrawable p, int zOrder )
+		{
+			Add( p, XAxisPosition.Bottom, YAxisPosition.Left, zOrder );
+		}
+
+
+        /// <summary>
+        /// Adds a drawable object to the plot surface against the specified axes with
+        /// z-order of 0. If the object is an IPlot, the PlotSurface2D axes will also
+        /// be updated.
+        /// </summary>
+        /// <param name="p">the IDrawable object to add to the plot surface</param>
+        /// <param name="xp">the x-axis to add the plot against.</param>
+        /// <param name="yp">the y-axis to add the plot against.</param>
+        public void Add(IDrawable p, XAxisPosition xp, YAxisPosition yp)
+        {
+            Add(p, xp, yp, 0);
+        }
+        
+        
+        /// <summary>
+        /// Adds a drawable object to the plot surface against the specified axes. If
+		/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">the IDrawable object to add to the plot surface</param>
+		/// <param name="xp">the x-axis to add the plot against.</param>
+		/// <param name="yp">the y-axis to add the plot against.</param>
+		/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+		public void Add( IDrawable p, XAxisPosition xp, YAxisPosition yp, int zOrder )
+		{
+			drawables_.Add( p );
+			xAxisPositions_.Add( xp );
+			yAxisPositions_.Add( yp );
+            zPositions_.Add((double)zOrder);
+            // fraction is to make key unique. With 10 million plots at same z, this buggers up.. 
+            double fraction = (double)(++uniqueCounter_)/10000000.0f; 
+            ordering_.Add( (double)zOrder + fraction, drawables_.Count - 1 );
+            
+            // if p is just an IDrawable, then it can't affect the axes.
+			if ( p is IPlot )
+			{
+				UpdateAxes( false );
+			}
+
+		}
+
+        private int uniqueCounter_ = 0;
+
+
+        private void UpdateAxes( bool recalculateAll )
+		{
+            if (drawables_.Count != xAxisPositions_.Count || drawables_.Count != yAxisPositions_.Count)
+            {
+                throw new NPlotException("plots and axis position arrays our of sync");
+            }
+
+            int position = 0;
+
+            // if we're not recalculating axes using all iplots then set
+            // position to last one in list.
+            if (!recalculateAll)
+            {
+                position = drawables_.Count - 1;
+                if (position < 0) position = 0;
+            }
+
+            if (recalculateAll)
+            {
+                this.xAxis1_ = null;
+                this.yAxis1_ = null;
+                this.xAxis2_ = null;
+                this.yAxis2_ = null;
+            }
+
+            for (int i = position; i < drawables_.Count; ++i)
+            {
+
+                // only update axes if this drawable is an IPlot.
+                if (!(drawables_[position] is IPlot))
+                    continue;
+
+                IPlot p = (IPlot)drawables_[position];
+                XAxisPosition xap = (XAxisPosition)xAxisPositions_[position];
+                YAxisPosition yap = (YAxisPosition)yAxisPositions_[position];
+
+                if (xap == XAxisPosition.Bottom)
+                {
+                    if (this.xAxis1_ == null)
+                    {
+                        this.xAxis1_ = p.SuggestXAxis();
+                        if (this.xAxis1_ != null)
+                        {
+                            this.xAxis1_.TicksAngle = -(float)Math.PI / 2.0f;
+                        }
+                    }
+                    else
+                    {
+                        this.xAxis1_.LUB(p.SuggestXAxis());
+                    }
+
+                    if (this.xAxis1_ != null)
+                    {
+                        this.xAxis1_.MinPhysicalLargeTickStep = 50;
+
+                        if (this.AutoScaleAutoGeneratedAxes)
+                        {
+                            this.xAxis1_.AutoScaleText = true;
+                            this.xAxis1_.AutoScaleTicks = true;
+                            this.xAxis1_.TicksIndependentOfPhysicalExtent = true;
+                        }
+                        else
+                        {
+                            this.xAxis1_.AutoScaleText = false;
+                            this.xAxis1_.AutoScaleTicks = false;
+                            this.xAxis1_.TicksIndependentOfPhysicalExtent = false;
+                        }
+                    }
+                }
+
+                if (xap == XAxisPosition.Top)
+                {
+                    if (this.xAxis2_ == null)
+                    {
+                        this.xAxis2_ = p.SuggestXAxis();
+                        if (this.xAxis2_ != null)
+                        {
+                            this.xAxis2_.TicksAngle = (float)Math.PI / 2.0f;
+                        }
+                    }
+                    else
+                    {
+                        this.xAxis2_.LUB(p.SuggestXAxis());
+                    }
+
+                    if (this.xAxis2_ != null)
+                    {
+                        this.xAxis2_.MinPhysicalLargeTickStep = 50;
+
+                        if (this.AutoScaleAutoGeneratedAxes)
+                        {
+                            this.xAxis2_.AutoScaleText = true;
+                            this.xAxis2_.AutoScaleTicks = true;
+                            this.xAxis2_.TicksIndependentOfPhysicalExtent = true;
+                        }
+                        else
+                        {
+                            this.xAxis2_.AutoScaleText = false;
+                            this.xAxis2_.AutoScaleTicks = false;
+                            this.xAxis2_.TicksIndependentOfPhysicalExtent = false;
+                        }
+                    }
+                }
+
+                if (yap == YAxisPosition.Left)
+                {
+                    if (this.yAxis1_ == null)
+                    {
+                        this.yAxis1_ = p.SuggestYAxis();
+                        if (this.yAxis1_ != null)
+                        {
+                            this.yAxis1_.TicksAngle = (float)Math.PI / 2.0f;
+                        }
+                    }
+                    else
+                    {
+                        this.yAxis1_.LUB(p.SuggestYAxis());
+                    }
+
+                    if (this.yAxis1_ != null)
+                    {
+                        if (this.AutoScaleAutoGeneratedAxes)
+                        {
+                            this.yAxis1_.AutoScaleText = true;
+                            this.yAxis1_.AutoScaleTicks = true;
+                            this.yAxis1_.TicksIndependentOfPhysicalExtent = true;
+                        }
+                        else
+                        {
+                            this.yAxis1_.AutoScaleText = false;
+                            this.yAxis1_.AutoScaleTicks = false;
+                            this.yAxis1_.TicksIndependentOfPhysicalExtent = false;
+                        }
+                    }
+                }
+
+                if (yap == YAxisPosition.Right)
+                {
+                    if (this.yAxis2_ == null)
+                    {
+                        this.yAxis2_ = p.SuggestYAxis();
+                        if (this.yAxis2_ != null)
+                        {
+                            this.yAxis2_.TicksAngle = -(float)Math.PI / 2.0f;
+                        }
+                    }
+                    else
+                    {
+                        this.yAxis2_.LUB(p.SuggestYAxis());
+                    }
+
+                    if (this.yAxis2_ != null)
+                    {
+                        if (this.AutoScaleAutoGeneratedAxes)
+                        {
+                            this.yAxis2_.AutoScaleText = true;
+                            this.yAxis2_.AutoScaleTicks = true;
+                            this.yAxis2_.TicksIndependentOfPhysicalExtent = true;
+                        }
+                        else
+                        {
+                            this.yAxis2_.AutoScaleText = false;
+                            this.yAxis2_.AutoScaleTicks = false;
+                            this.yAxis2_.TicksIndependentOfPhysicalExtent = false;
+                        }
+                    }
+
+                }
+            }
+
+        }
+
+
+		private void DetermineAxesToDraw( out Axis xAxis1, out Axis xAxis2, out Axis yAxis1, out Axis yAxis2 )
+		{
+			xAxis1 = this.xAxis1_;
+			xAxis2 = this.xAxis2_;
+			yAxis1 = this.yAxis1_;
+			yAxis2 = this.yAxis2_;
+
+			if (this.xAxis1_ == null)
+			{
+				if (this.xAxis2_ == null)
+				{
+					throw new NPlotException( "Error: No X-Axis specified" );
+				}
+				xAxis1 = (Axis)this.xAxis2_.Clone();
+				xAxis1.HideTickText = true;
+				xAxis1.TicksAngle = -(float)Math.PI / 2.0f;
+			}
+
+			if (this.xAxis2_ == null)
+			{
+				// don't need to check if xAxis1_ == null, as case already handled above.
+				xAxis2 = (Axis)this.xAxis1_.Clone();
+				xAxis2.HideTickText = true;
+				xAxis2.TicksAngle = (float)Math.PI / 2.0f;
+			}
+
+			if (this.yAxis1_ == null)
+			{
+				if (this.yAxis2_ == null)
+				{
+					throw new NPlotException( "Error: No Y-Axis specified" );
+				}
+				yAxis1 = (Axis)this.yAxis2_.Clone();
+				yAxis1.HideTickText = true;
+				yAxis1.TicksAngle = (float)Math.PI / 2.0f;
+			}
+
+			if (this.yAxis2_ == null)
+			{
+				// don't need to check if yAxis1_ == null, as case already handled above.
+				yAxis2 = (Axis)this.yAxis1_.Clone();
+				yAxis2.HideTickText = true;
+				yAxis2.TicksAngle = -(float)Math.PI / 2.0f;
+			}
+
+		}
+
+
+		private void DeterminePhysicalAxesToDraw( Rectangle bounds, 
+			Axis xAxis1, Axis xAxis2, Axis yAxis1, Axis yAxis2,
+			out PhysicalAxis pXAxis1, out PhysicalAxis pXAxis2, 
+			out PhysicalAxis pYAxis1, out PhysicalAxis pYAxis2 )
+		{
+
+			System.Drawing.Rectangle cb = bounds;
+
+			pXAxis1 = new PhysicalAxis( xAxis1,
+				new Point( cb.Left, cb.Bottom ), new Point( cb.Right, cb.Bottom ) );
+			pYAxis1 = new PhysicalAxis( yAxis1,
+				new Point( cb.Left, cb.Bottom ), new Point( cb.Left, cb.Top ) );
+			pXAxis2 = new PhysicalAxis( xAxis2,
+				new Point( cb.Left, cb.Top), new Point( cb.Right, cb.Top) );
+			pYAxis2 = new PhysicalAxis( yAxis2,
+				new Point( cb.Right, cb.Bottom ), new Point( cb.Right, cb.Top ) );
+
+			int bottomIndent = padding_;
+			if (!pXAxis1.Axis.Hidden) 
+			{
+				// evaluate its bounding box
+				Rectangle bb = pXAxis1.GetBoundingBox();
+				// finally determine its indentation from the bottom
+				bottomIndent = bottomIndent + bb.Bottom - cb.Bottom;
+			}
+
+			int leftIndent = padding_;
+			if (!pYAxis1.Axis.Hidden) 
+			{
+				// evaluate its bounding box
+				Rectangle bb = pYAxis1.GetBoundingBox();
+				// finally determine its indentation from the left
+				leftIndent = leftIndent - bb.Left + cb.Left;
+			}
+
+			int topIndent = padding_;
+			float scale = this.DetermineScaleFactor( bounds.Width, bounds.Height );
+			int titleHeight;
+			if (this.AutoScaleTitle)
+			{
+				titleHeight = Utils.ScaleFont(titleFont_, scale).Height;
+			}
+			else
+			{
+				titleHeight = titleFont_.Height;
+			}
+
+			//count number of new lines in title.
+			int nlCount = 0;
+			for (int i=0; i<title_.Length; ++i)
+			{
+				if (title_[i] == '\n')
+					nlCount += 1;
+			}
+			titleHeight = (int)( ((float)nlCount*0.75 + 1.0f) * (float)titleHeight);
+
+			if (!pXAxis2.Axis.Hidden)  
+			{
+				// evaluate its bounding box
+				Rectangle bb = pXAxis2.GetBoundingBox();
+				topIndent = topIndent - bb.Top + cb.Top;
+
+				// finally determine its indentation from the top
+				// correct top indendation to take into account plot title
+				if (title_ != "" )
+				{
+					topIndent += (int)(titleHeight * 1.3f);
+				}
+			}
+
+			int rightIndent = padding_;
+			if (!pYAxis2.Axis.Hidden) 
+			{
+				// evaluate its bounding box
+				Rectangle bb = pYAxis2.GetBoundingBox();
+
+				// finally determine its indentation from the right
+				rightIndent = (int)(rightIndent + bb.Right-cb.Right);
+			}
+
+			// now we have all the default calculated positions and we can proceed to
+			// "move" the axes to their right places
+
+			// primary axes (bottom, left)
+			pXAxis1.PhysicalMin = new Point( cb.Left+leftIndent, cb.Bottom-bottomIndent );
+			pXAxis1.PhysicalMax = new Point( cb.Right-rightIndent, cb.Bottom-bottomIndent );
+			pYAxis1.PhysicalMin = new Point( cb.Left+leftIndent, cb.Bottom-bottomIndent );
+			pYAxis1.PhysicalMax = new Point( cb.Left+leftIndent, cb.Top+topIndent );
+
+			// secondary axes (top, right)
+			pXAxis2.PhysicalMin = new Point( cb.Left+leftIndent, cb.Top+topIndent );
+			pXAxis2.PhysicalMax = new Point( cb.Right-rightIndent, cb.Top+topIndent );
+			pYAxis2.PhysicalMin = new Point( cb.Right-rightIndent, cb.Bottom-bottomIndent );
+			pYAxis2.PhysicalMax = new Point( cb.Right-rightIndent, cb.Top+topIndent );
+
+		}
+
+
+
+		/// <summary>
+		/// Draw the the PlotSurface2D and all contents [axes, drawables, and legend] on the 
+		/// supplied graphics surface.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="bounds">A bounding box on this surface that denotes the area on the
+		/// surface to confine drawing to.</param>
+		public void Draw( Graphics g, Rectangle bounds )
+		{
+			// determine font sizes and tick scale factor.
+			float scale = DetermineScaleFactor( bounds.Width, bounds.Height );
+
+			// if there is nothing to plot, return.
+			if ( drawables_.Count == 0 )
+			{
+				// draw title
+				float x_center = (bounds.Left + bounds.Right)/2.0f;
+				float y_center = (bounds.Top + bounds.Bottom)/2.0f;
+				Font scaled_font;
+				if (this.AutoScaleTitle)
+				{
+					scaled_font = Utils.ScaleFont( titleFont_, scale );
+				}
+				else
+				{
+					scaled_font = titleFont_;
+				}
+				g.DrawString( title_, scaled_font, this.titleBrush_, new PointF(x_center,y_center), titleDrawFormat_ );
+
+				return;
+			}
+
+			// determine the [non physical] axes to draw based on the axis properties set.
+			Axis xAxis1 = null;
+			Axis xAxis2 = null;
+			Axis yAxis1 = null;
+			Axis yAxis2 = null;
+			this.DetermineAxesToDraw( out xAxis1, out xAxis2, out yAxis1, out yAxis2 );
+
+			// apply scale factor to axes as desired.
+
+			if (xAxis1.AutoScaleTicks) 
+				xAxis1.TickScale = scale;
+			if (xAxis1.AutoScaleText)
+				xAxis1.FontScale = scale;
+			if (yAxis1.AutoScaleTicks)
+				yAxis1.TickScale = scale;
+			if (yAxis1.AutoScaleText)
+				yAxis1.FontScale = scale;
+			if (xAxis2.AutoScaleTicks)
+				xAxis2.TickScale = scale;
+			if (xAxis2.AutoScaleText)
+				xAxis2.FontScale = scale;
+			if (yAxis2.AutoScaleTicks)
+				yAxis2.TickScale = scale;
+			if (yAxis2.AutoScaleText)
+				yAxis2.FontScale = scale;
+
+			// determine the default physical positioning of those axes.
+			PhysicalAxis pXAxis1 = null;
+			PhysicalAxis pYAxis1 = null;
+			PhysicalAxis pXAxis2 = null;
+			PhysicalAxis pYAxis2 = null;
+			this.DeterminePhysicalAxesToDraw( 
+				bounds, xAxis1, xAxis2, yAxis1, yAxis2,
+				out pXAxis1, out pXAxis2, out pYAxis1, out pYAxis2 );
+
+			float oldXAxis2Height = pXAxis2.PhysicalMin.Y;
+
+			// Apply axes constraints
+			for (int i=0; i<axesConstraints_.Count; ++i)
+			{
+				((AxesConstraint)axesConstraints_[i]).ApplyConstraint( 
+					pXAxis1, pYAxis1, pXAxis2, pYAxis2 );
+			}
+
+			/////////////////////////////////////////////////////////////////////////
+			// draw legend if have one.
+			// Note: this will update axes if necessary. 
+
+			Point legendPosition = new Point(0,0);
+			if (this.legend_ != null)
+			{
+				legend_.UpdateAxesPositions( 
+					pXAxis1, pYAxis1, pXAxis2, pYAxis2,
+					this.drawables_, scale, this.padding_, bounds, 
+					out legendPosition );
+			}
+
+			float newXAxis2Height = pXAxis2.PhysicalMin.Y;
+
+			float titleExtraOffset = oldXAxis2Height - newXAxis2Height;
+	
+			// now we are ready to define the bounding box for the plot area (to use in clipping
+			// operations.
+			plotAreaBoundingBoxCache_ = new Rectangle( 
+				Math.Min( pXAxis1.PhysicalMin.X, pXAxis1.PhysicalMax.X ),
+				Math.Min( pYAxis1.PhysicalMax.Y, pYAxis1.PhysicalMin.Y ),
+				Math.Abs( pXAxis1.PhysicalMax.X - pXAxis1.PhysicalMin.X + 1 ),
+				Math.Abs( pYAxis1.PhysicalMin.Y - pYAxis1.PhysicalMax.Y + 1 )
+			);
+			bbXAxis1Cache_ = pXAxis1.GetBoundingBox();
+			bbXAxis2Cache_ = pXAxis2.GetBoundingBox();
+			bbYAxis1Cache_ = pYAxis1.GetBoundingBox();
+			bbYAxis2Cache_ = pYAxis2.GetBoundingBox();
+
+			// Fill in the background. 
+			if ( this.plotBackColor_ != null )
+			{
+				g.FillRectangle(
+					new System.Drawing.SolidBrush( (Color)this.plotBackColor_ ),
+					(Rectangle)plotAreaBoundingBoxCache_ );
+			}
+			else if (this.plotBackBrush_ != null)
+			{
+				g.FillRectangle( 
+					this.plotBackBrush_.Get( (Rectangle)plotAreaBoundingBoxCache_ ),
+					(Rectangle)plotAreaBoundingBoxCache_ );
+			}
+			else if (this.plotBackImage_ != null)
+			{
+				g.DrawImage( 
+					Utils.TiledImage( this.plotBackImage_ , new Size( 
+						((Rectangle)plotAreaBoundingBoxCache_).Width,
+						((Rectangle)plotAreaBoundingBoxCache_).Height ) ), 
+					(Rectangle)plotAreaBoundingBoxCache_ );
+			}
+
+			// draw title
+			float xt = (pXAxis2.PhysicalMax.X + pXAxis2.PhysicalMin.X)/2.0f;
+			float yt = bounds.Top + this.padding_ - titleExtraOffset;
+			Font scaledFont;
+			if (this.AutoScaleTitle)
+			{
+				scaledFont = Utils.ScaleFont( titleFont_, scale );
+			}
+			else
+			{
+				scaledFont = titleFont_;
+			}
+			g.DrawString( title_, scaledFont, this.titleBrush_,	new PointF(xt,yt), titleDrawFormat_ );
+			
+			//count number of new lines in title.
+			int nlCount = 0;
+			for (int i=0; i<title_.Length; ++i)
+			{
+				if (title_[i] == '\n')
+					nlCount += 1;
+			}
+
+			SizeF s = g.MeasureString(title_,scaledFont);
+			bbTitleCache_ = new Rectangle( (int)(xt-s.Width/2), (int)(yt), (int)(s.Width), (int)(s.Height)*(nlCount+1) );
+
+			// draw drawables..
+			System.Drawing.Drawing2D.SmoothingMode smoothSave = g.SmoothingMode;
+
+			g.SmoothingMode = this.smoothingMode_;
+
+			bool legendDrawn = false;
+
+			for ( int i_o = 0; i_o < ordering_.Count; ++i_o )
+			{
+	
+                int i = (int)ordering_.GetByIndex(i_o);
+				double zOrder = (double)ordering_.GetKey( i_o );
+				if (zOrder > this.legendZOrder_)
+				{
+					// draw legend.
+					if ( !legendDrawn && this.legend_ != null )
+					{
+						legend_.Draw( g, legendPosition, this.drawables_, scale );
+						legendDrawn = true;
+					}
+				}
+
+                IDrawable drawable = (IDrawable)drawables_[i];
+				XAxisPosition xap = (XAxisPosition)xAxisPositions_[i];
+				YAxisPosition yap = (YAxisPosition)yAxisPositions_[i];
+
+				PhysicalAxis drawXAxis;
+				PhysicalAxis drawYAxis;
+
+				if ( xap == XAxisPosition.Bottom )
+				{
+					drawXAxis = pXAxis1;
+				}
+				else
+				{
+					drawXAxis = pXAxis2;
+				}
+
+				if ( yap == YAxisPosition.Left )
+				{
+					drawYAxis = pYAxis1;
+				}
+				else
+				{
+					drawYAxis = pYAxis2;
+				}
+	
+				// set the clipping region.. (necessary for zoom)
+				g.Clip = new Region((Rectangle)plotAreaBoundingBoxCache_);
+				// plot.
+				drawable.Draw( g, drawXAxis, drawYAxis );
+				// reset it..
+				g.ResetClip();
+			}
+			
+			if ( !legendDrawn && this.legend_ != null )
+			{
+				legend_.Draw( g, legendPosition, this.drawables_, scale );
+			}
+
+			// cache the physical axes we used on this draw;
+			this.pXAxis1Cache_ = pXAxis1;
+			this.pYAxis1Cache_ = pYAxis1;
+			this.pXAxis2Cache_ = pXAxis2;
+			this.pYAxis2Cache_ = pYAxis2;
+
+			g.SmoothingMode = smoothSave;
+
+			// now draw axes.
+			Rectangle axisBounds;
+			pXAxis1.Draw( g, out axisBounds );
+			pXAxis2.Draw( g, out axisBounds );
+			pYAxis1.Draw( g, out axisBounds );
+			pYAxis2.Draw( g, out axisBounds );
+
+#if DEBUG_BOUNDING_BOXES
+			g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbXAxis1Cache_ );
+			g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbXAxis2Cache_ );
+			g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbYAxis1Cache_ );
+			g.DrawRectangle( new Pen(Color.Orange), (Rectangle) bbYAxis2Cache_ );
+			g.DrawRectangle( new Pen(Color.Red,5.0F),(Rectangle) plotAreaBoundingBoxCache_);
+			//if(this.ShowLegend)g.DrawRectangle( new Pen(Color.Chocolate, 3.0F), (Rectangle) bbLegendCache_);
+			g.DrawRectangle( new Pen(Color.DeepPink,2.0F), (Rectangle) bbTitleCache_);
+#endif
+
+		}
+
+
+		/// <summary>
+		/// Clears the plot and resets all state to the default.
+		/// </summary>
+		public void Clear()
+		{
+			Init();
+		}
+
+
+		/// <summary>
+		/// Legend to use. If this property is null [default], then the plot
+		/// surface will have no corresponding legend.
+		/// </summary>
+		public NPlot.Legend Legend
+		{
+			get
+			{
+				return this.legend_;
+			}
+			set
+			{
+				this.legend_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Add an axis constraint to the plot surface. Axes constraints give you 
+		/// control over where NPlot positions each axes, and the world - pixel
+		/// ratio.
+		/// </summary>
+		/// <param name="constraint">The axis constraint to add.</param>
+		public void AddAxesConstraint( AxesConstraint constraint )
+		{
+			this.axesConstraints_.Add( constraint );
+		}
+
+
+		/// <summary>
+		/// Whether or not the title will be scaled according to size of the plot surface.
+		/// </summary>
+		public bool AutoScaleTitle
+		{
+			get
+			{
+				return autoScaleTitle_;
+			}
+			set
+			{
+				autoScaleTitle_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// When plots are added to the plot surface, the axes they are attached to
+		/// are immediately modified to reflect data of the plot. If 
+		/// AutoScaleAutoGeneratedAxes is true when a plot is added, the axes will
+		/// be turned in to auto scaling ones if they are not already [tick marks,
+		/// tick text and label size scaled to size of plot surface]. If false,
+		/// axes will not be autoscaling.
+		/// </summary>
+		public bool AutoScaleAutoGeneratedAxes
+		{
+			get
+			{
+				return autoScaleAutoGeneratedAxes_;
+			}
+			set
+			{
+				autoScaleAutoGeneratedAxes_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Remove a drawable object. 
+		/// Note that axes are not updated.
+		/// </summary>
+		/// <param name="p">Drawable to remove.</param>
+		/// <param name="updateAxes">if true, the axes are updated.</param>
+		public void Remove( IDrawable p, bool updateAxes ) 
+		{
+			int index = drawables_.IndexOf( p );
+			if (index < 0)
+				return;
+			drawables_.RemoveAt( index );
+			xAxisPositions_.RemoveAt( index );
+			yAxisPositions_.RemoveAt( index );
+            zPositions_.RemoveAt(index);
+
+            if (updateAxes)
+            {
+                this.UpdateAxes(true);
+            }
+
+            this.RefreshZOrdering();
+        }
+
+
+		/// <summary>
+		/// If a plot is removed, then the ordering_ list needs to be 
+		/// recalculated. 
+		/// </summary>
+		private void RefreshZOrdering() 
+		{
+			uniqueCounter_ = 0;
+			ordering_ = new SortedList();
+			for (int i = 0; i < zPositions_.Count; ++i) 
+			{
+				double zpos = Convert.ToDouble(zPositions_[i]);
+				double fraction = (double)(++uniqueCounter_) / 10000000.0f;
+				double d = zpos + fraction;
+				ordering_.Add(d, i);
+			}
+		}
+
+
+
+        /// <summary>
+        /// Gets an array list containing all drawables currently added to the PlotSurface2D.
+        /// </summary>
+        public ArrayList Drawables
+        {
+			get
+			{
+				return this.drawables_;
+			}
+		}
+
+
+		/// <summary>
+		/// Returns the x-axis associated with a given plot.
+		/// </summary>
+		/// <param name="plot">the plot to get associated x-axis.</param>
+		/// <returns>the axis associated with the plot.</returns>
+		public Axis WhichXAxis( IPlot plot )
+		{
+			int index = drawables_.IndexOf( plot );
+			XAxisPosition p = (XAxisPosition)xAxisPositions_[index];
+			if ( p == XAxisPosition.Bottom )
+				return this.xAxis1_;
+			else
+				return this.xAxis2_;
+		}
+
+
+		/// <summary>
+		/// Returns the y-axis associated with a given plot.
+		/// </summary>
+		/// <param name="plot">the plot to get associated y-axis.</param>
+		/// <returns>the axis associated with the plot.</returns>
+		public Axis WhichYAxis( IPlot plot )
+		{
+			int index = drawables_.IndexOf( plot );
+			YAxisPosition p = (YAxisPosition)yAxisPositions_[index];
+			if ( p == YAxisPosition.Left )
+				return this.yAxis1_;
+			else
+				return this.yAxis2_;
+		}
+
+
+		/// <summary>
+		/// Setting this value determines the order (relative to IDrawables added to the plot surface)
+		/// that the legend is drawn.
+		/// </summary>
+		public int LegendZOrder
+		{
+			get
+			{
+				return legendZOrder_;
+			}
+			set
+			{
+				legendZOrder_ = value;
+			}
+		}
+		int legendZOrder_ = -1;
+
+
+    } 
+} 
+
+
diff --git a/nplot/nplot/PlotSurface2Dnew.cs b/nplot/nplot/PlotSurface2Dnew.cs
new file mode 100644
index 0000000..94aa9f0
--- /dev/null
+++ b/nplot/nplot/PlotSurface2Dnew.cs
@@ -0,0 +1,439 @@
+// ******** experimental ******** 
+/*
+using System;
+using System.Xml;
+using System.Data;
+using System.Collections;
+using System.IO;
+using System.Reflection;
+using System.Drawing;
+
+namespace NPlot
+{
+
+	class PlotSurface2Dnew
+	{
+
+		private ArrayList axisLines_;
+		private ArrayList axisDefinitions_;
+		private ArrayList drawables_;
+		private ArrayList xAxisPositions_;
+		private ArrayList yAxisPositions_;
+
+		private System.Collections.Hashtable axes_;
+
+		/// <summary>
+		/// PlotSurface2D styles supplied with the library.
+		/// </summary>
+		public enum PlotSurfaceStyle
+		{
+			Standard,
+			CrossedAxes,
+			OppositeCloned
+		}
+
+		private class AxisDefinition
+		{
+			public string Name = "";
+			public AxisLine axisLine = null;
+			public int Min = 0;
+			public int Max = 100;
+		}
+
+		private class AxisLine
+		{
+			public enum OrientationType
+			{
+				Horizontal,
+				Vertical
+			}
+
+			public string Name = "";
+			public OrientationType Orientation = OrientationType.Horizontal;
+			public float Position = 0;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public PlotSurface2Dnew()
+		{
+			Init();
+		}
+
+
+		private AxisDefinition parseAxis( XmlNode node )
+		{
+			// get Parameters 
+			AxisDefinition d = new AxisDefinition();
+
+			for (int i=0; i<node.Attributes.Count; ++i)
+			{
+				XmlAttribute a = node.Attributes[i];
+				switch (a.Name.ToLower())
+				{
+					case "name":
+						d.Name = a.Value;
+						break;
+
+					case "axisline":
+						bool found = false;
+						for (int j=0; j<axisLines_.Count; ++j)
+						{
+							if (((AxisLine)axisLines_[j]).Name == a.Value)
+							{
+								d.axisLine = (AxisLine)axisLines_[j];
+								found = true;
+							}
+						}
+						if (!found)
+						{
+							ErrorHandler.Instance.CriticalError( "AxisLine not found" );
+						}
+						break;
+
+					case "physicalmin":
+						try
+						{
+							d.Min = Convert.ToInt32( a.Value );
+						}
+						catch
+						{
+							ErrorHandler.Instance.CriticalError( "min value not numeric" );
+						}
+						if (d.Min< 0 || d.Min > 100)
+						{
+							ErrorHandler.Instance.CriticalError( "min value must be between 0 and 100" );
+						}
+						break;
+
+					case "physicalmax":
+						try
+						{
+							d.Max = Convert.ToInt32( a.Value );
+						}
+						catch
+						{
+							ErrorHandler.Instance.CriticalError( "max value not numeric" );
+						}
+						if (d.Max < 0 || d.Max > 100)
+						{
+							ErrorHandler.Instance.CriticalError( "max value must be between 0 and 100" );
+						}
+						break;
+
+					default:
+						ErrorHandler.Instance.CriticalError( "unknown attribute: " + a.Name );
+						break;
+
+				}
+			}
+
+			return d;
+		}
+
+
+		private AxisLine parseAxisLine( XmlNode node )
+		{
+		
+			AxisLine l = new AxisLine();
+
+			for (int i=0; i<node.Attributes.Count; ++i)
+			{
+				XmlAttribute a = node.Attributes[i];
+				switch (a.Name.ToLower())
+				{
+					case "name":
+						l.Name = a.Value;
+						break;
+
+					case "orientation":
+						if (a.Value.ToLower() == "horizontal")
+							l.Orientation = AxisLine.OrientationType.Horizontal;
+						else if (a.Value.ToLower() == "vertical")
+							l.Orientation = AxisLine.OrientationType.Vertical;
+						else
+						{
+							ErrorHandler.Instance.CriticalError( "Unexpected orientation :" + a.Value.ToString() );
+						}	
+						break;
+
+					case "position":
+						try
+						{
+							l.Position = Convert.ToInt32( a.Value );
+						}
+						catch
+						{
+							ErrorHandler.Instance.CriticalError( "position not numeric" );
+						}
+						if (l.Position < 0 || l.Position > 100)
+						{
+							ErrorHandler.Instance.CriticalError( "axis position must be between 0 and 100" );
+						}
+						break;
+									
+					default:
+						ErrorHandler.Instance.CriticalError( "unknown axis line attribute: " + a.Name );
+						break;
+				}
+			}
+					
+			return l;
+		}
+
+
+		public void SetDefinition( )
+		{
+
+			Assembly ass = Assembly.GetExecutingAssembly();
+			System.IO.Stream xmlStream = 
+				ass.GetManifestResourceStream( "NPlot.PlotSurfaceDefinitions.OppositeCloned.xml" );
+
+			// load xml
+
+			XmlDocument xmlDoc = null;
+
+			try
+			{
+				xmlDoc = new XmlDocument();
+				xmlDoc.Load( xmlStream );
+			}
+			catch
+			{
+				ErrorHandler.Instance.CriticalError( "PlotSurface2D definition file malformed" );
+			}
+
+			XmlElement el = xmlDoc.DocumentElement;
+			
+			// check we're a PlotSurface2D.
+			if (el.Name.ToLower() != "plotsurface2d")
+			{
+				ErrorHandler.Instance.CriticalError( "Root element must be PlotSurface2D" );
+				return;
+			}
+
+			// enforce only one axis set for now.
+			if (el.ChildNodes.Count != 1)
+			{
+				ErrorHandler.Instance.CriticalError( "need one and only one axis set." );
+				return;
+			}
+
+			// loop around all AxisSets.
+			foreach (XmlNode n in el.ChildNodes)
+			{
+				if (n.Name.ToLower() != "axisset")
+				{
+					ErrorHandler.Instance.CriticalError( "Expected AxisSet node" );
+					return;
+				}
+
+				// loop around all nodes in this axis set.
+				foreach (XmlNode n2 in n.ChildNodes)
+				{
+					switch (n2.Name.ToLower())
+					{
+						case "axisline":
+						{
+							axisLines_.Add( parseAxisLine(n2) ); 
+							break;
+						}
+						case "axis":
+						{
+							axisDefinitions_.Add( parseAxis(n2) );
+							break;
+						}
+						default:
+						{
+							ErrorHandler.Instance.CriticalError( "Unexpected node type encountered: " + n2.Name );
+							return;
+						}
+					}
+				} // end loop around each node in axis set
+
+				// add all axes to class
+				for (int i=0; i<axisDefinitions_.Count; ++i)
+				{
+					//axes_.Add( ((AxisDefinition)axisDefinitions_[i]).Name, null );
+				}
+
+			} // end loop around axis sets.
+			
+		}
+
+
+		/// <summary>
+		/// The distance in pixels to leave between of the edge of the bounding rectangle
+		/// supplied to the Draw method, and the markings that make up the plot.
+		/// </summary>
+		public int Padding
+		{
+			get
+			{
+				return padding_;
+			}
+			set
+			{
+				padding_ = value;
+			}
+		}
+		private int padding_;
+
+
+		private void Init()
+		{
+			axes_ = null;
+
+			drawables_ = new ArrayList();
+			xAxisPositions_ = new ArrayList();
+			yAxisPositions_ = new ArrayList();
+			axisLines_ = new ArrayList();
+			axisDefinitions_ = new ArrayList();
+
+			padding_ = 10;
+		}
+
+
+		/// <summary>
+		/// Draw the the PlotSurface2D and all contents [axes, drawables, and legend] on the 
+		/// supplied graphics surface.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="bounds">A bounding box on this surface that denotes the area on the
+		/// surface to confine drawing to.</param>
+		public void Draw( Graphics g, Rectangle bounds )
+		{
+			Rectangle realBounds = new Rectangle( 
+				bounds.X + padding_ / 2, 
+				bounds.Y + padding_ / 2,
+				bounds.Width - padding_,
+				bounds.Height - padding_
+			);
+
+			// create initial set of physical axes. These will
+			// be moved later to ensure everything is drawn ok.
+			ArrayList physicalAxes = new ArrayList();
+
+			for (int i=0; i<axisDefinitions_.Count; ++i)
+			{
+				AxisDefinition d = (AxisDefinition)axisDefinitions_[i];
+				
+				Point physicalMin = new Point(0,0);
+				Point physicalMax = new Point(0,0);
+
+				if (d.axisLine.Orientation == AxisLine.OrientationType.Horizontal)
+				{
+					int yPos = (int)(realBounds.Top + ((float)d.axisLine.Position/100.0 * realBounds.Height));
+
+					physicalMin.X = realBounds.Left;
+					physicalMin.Y = yPos;
+					physicalMax.X = realBounds.Right;
+					physicalMax.Y = yPos;
+				}
+				else
+				{
+					int xPos = (int)(realBounds.Left + ((float)d.axisLine.Position/100.0 * realBounds.Width));
+
+					physicalMin.X = xPos;
+					physicalMin.Y = realBounds.Bottom;
+					physicalMax.X = xPos;
+					physicalMax.Y = realBounds.Top;
+				}
+
+				physicalAxes.Add( new PhysicalAxis( new LinearAxis(0.0,1.0), physicalMin, physicalMax ) );
+			}
+
+			for (int i=0; i<physicalAxes.Count; ++i)
+			{
+				Rectangle axisBounds;
+				((PhysicalAxis)physicalAxes[i]).Draw( g, out axisBounds );
+			}
+
+
+		}
+
+
+		private void UpdateAxes()
+		{
+			// make sure drawable lists exist.
+			if (drawables_.Count==0 || xAxisPositions_.Count==0 || yAxisPositions_.Count==0)
+			{
+				ErrorHandler.Instance.ContinuingError( "UpdateAxes called from function other than Add." );
+			}
+
+			int last = drawables_.Count - 1;
+			
+			if ( last != xAxisPositions_.Count-1 || last != yAxisPositions_.Count-1 )
+			{
+				ErrorHandler.Instance.CriticalError( "plots and axis position arrays our of sync" );
+			}
+
+
+			// make sure axes exit.
+			AxisDefinition xAxisDefinition = null;
+			AxisDefinition yAxisDefinition = null;
+			for (int i=0; i<this.axisDefinitions_.Count; ++i)
+			{
+				AxisDefinition def = (AxisDefinition)axisDefinitions_[i];
+				if (def.Name == (string)xAxisPositions_[last])
+					xAxisDefinition = def;
+
+				if (def.Name == (string)yAxisPositions_[last])
+					yAxisDefinition = def;	
+			}
+
+			if (xAxisDefinition == null || yAxisDefinition == null)
+			{
+				ErrorHandler.Instance.CriticalError( "Axis does not exist." );
+			}
+
+			IPlot p = (IPlot)drawables_[last];
+
+			// update x axis.
+			if (axes_[xAxisDefinition.Name] == null)
+			{
+				this.axes_[xAxisDefinition.Name] = p.SuggestXAxis();
+			}
+			else
+			{
+				((Axis)this.axes_[xAxisDefinition.Name]).LUB( p.SuggestXAxis() );
+			}
+
+			// update y axis. 
+			if (axes_[yAxisDefinition.Name] == null)
+			{
+				this.axes_[yAxisDefinition.Name] = p.SuggestYAxis();
+			}
+			else
+			{
+				((Axis)this.axes_[yAxisDefinition.Name]).LUB( p.SuggestYAxis() );
+			}
+
+		}
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface against the specified axes. If
+		/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">the IDrawable object to add to the plot surface</param>
+		/// <param name="xAxis">the x-axis to add the plot against.</param>
+		/// <param name="yAxis">the y-axis to add the plot against.</param>
+		public void Add( IDrawable p, string xAxis, string yAxis )
+		{
+			drawables_.Add( p );
+			xAxisPositions_.Add( xAxis );
+			yAxisPositions_.Add( yAxis );
+
+			if ( p is IPlot )
+			{
+				this.UpdateAxes();
+			}
+		}
+
+	}
+
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/PlotSurface3D.cs b/nplot/nplot/PlotSurface3D.cs
new file mode 100644
index 0000000..d6571b3
--- /dev/null
+++ b/nplot/nplot/PlotSurface3D.cs
@@ -0,0 +1,64 @@
+// ******** experimental ******** 
+
+/*
+NPlot - A charting library for .NET
+
+PlotSurface3D.cs
+Copyright (C) 2003
+Matt Howlett
+
+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 following text in 
+   the documentation and / or other materials provided with the 
+   distribution: 
+   
+   "This product includes software developed as part of 
+   the NPlot charting library project available from: 
+   http://www.nplot.com/"; 
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+$Id: PlotSurface3D.cs,v 1.9 2004/11/17 10:39:19 mhowlett Exp $
+
+*/
+/*
+
+using System;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// TODO
+	/// </summary>
+	public class PlotSurface3D : IPlotSurface3D
+	{
+
+		/// <summary>
+		/// TODO
+		/// </summary>
+		public PlotSurface3D()
+		{
+		}
+
+	}
+}
+
+*/
\ No newline at end of file
diff --git a/nplot/nplot/PointD.cs b/nplot/nplot/PointD.cs
new file mode 100644
index 0000000..91c4c2c
--- /dev/null
+++ b/nplot/nplot/PointD.cs
@@ -0,0 +1,91 @@
+/*
+NPlot - A charting library for .NET
+
+PointD.cs
+Copyright (C) 2003-2004
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+
+*/
+
+namespace NPlot
+{
+	/// <summary>
+	/// Represtents a point in two-dimensional space. Used for representation
+	/// of points world coordinates.
+	/// </summary>
+	public struct PointD
+	{
+		/// <summary>
+		/// X-Coordinate of the point.
+		/// </summary>
+		public double X;
+
+		/// <summary>
+		/// Y-Coordinate of the point.
+		/// </summary>
+		public double Y;
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="x">X-Coordinate of the point.</param>
+		/// <param name="y">Y-Coordinate of the point.</param>
+		public PointD( double x, double y )
+		{
+			X = x;
+			Y = y;
+		}
+
+		/// <summary>
+		/// returns a string representation of the point.
+		/// </summary>
+		/// <returns>string representation of the point.</returns>
+		public override string ToString()
+		{
+			return X.ToString() + "\t" + Y.ToString();
+		}
+
+	}
+}
diff --git a/nplot/nplot/PointD3D.cs b/nplot/nplot/PointD3D.cs
new file mode 100644
index 0000000..b56c2b6
--- /dev/null
+++ b/nplot/nplot/PointD3D.cs
@@ -0,0 +1,45 @@
+// ******** experimental ******** 
+/*
+using System;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Represtents a point in three-dimensional space. Used for representation
+	/// of points world coordinates.
+	/// </summary>
+	public struct PointD3D
+	{
+
+		/// <summary>
+		/// X-Coordinate of the point.
+		/// </summary>
+		public double X;
+
+		/// <summary>
+		/// Y-Coordinate of the point.
+		/// </summary>
+		public double Y;
+
+		/// <summary>
+		/// Z-Coordinate of the point.
+		/// </summary>
+		public double Z;
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="x">X-Coordinate of the point.</param>
+		/// <param name="y">Y-Coordinate of the point.</param>
+		/// <param name="z">Z-Coordinate of the point.</param>
+		public PointD3D( double x, double y, double z )
+		{
+			X = x;
+			Y = y;
+			Z = z;
+		}
+
+	}
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/PointPlot.cs b/nplot/nplot/PointPlot.cs
new file mode 100644
index 0000000..423599b
--- /dev/null
+++ b/nplot/nplot/PointPlot.cs
@@ -0,0 +1,182 @@
+/*
+NPlot - A charting library for .NET
+
+PointPlot.cs
+Copyright (C) 2003
+Matt Howlett, Paolo Pierini
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 drawing data as a series of points.
+	/// </summary>
+	public class PointPlot : BaseSequencePlot, ISequencePlot, IPlot
+	{
+		private Marker marker_;
+
+		/// <summary>
+		/// Default Constructor
+		/// </summary>
+		public PointPlot()
+		{
+			marker_ = new Marker();
+		}
+
+		/// <summary>
+		/// Constructor for the marker plot.
+		/// </summary>
+		/// <param name="marker">The marker to use.</param>
+		public PointPlot( Marker marker )
+		{
+			marker_ = marker;
+		}
+
+
+		/// <summary>
+		/// Draws the point plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			SequenceAdapter data_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+            float leftCutoff_ = xAxis.PhysicalMin.X - marker_.Size;
+            float rightCutoff_ = xAxis.PhysicalMax.X + marker_.Size;
+
+			for (int i=0; i<data_.Count; ++i)
+			{
+				if ( !Double.IsNaN(data_[i].X) && !Double.IsNaN(data_[i].Y) )
+				{
+					PointF xPos = xAxis.WorldToPhysical( data_[i].X, false);
+                    if (xPos.X < leftCutoff_ || rightCutoff_ < xPos.X)
+						continue;
+
+					PointF yPos = yAxis.WorldToPhysical( data_[i].Y, false);
+					marker_.Draw( g, (int)xPos.X, (int)yPos.Y );
+					if (marker_.DropLine)
+					{
+						PointD yMin = new PointD( data_[i].X, Math.Max( 0.0f, yAxis.Axis.WorldMin ) );
+						PointF yStart = yAxis.WorldToPhysical( yMin.Y, false );
+						g.DrawLine( marker_.Pen, new Point((int)xPos.X,(int)yStart.Y), new Point((int)xPos.X,(int)yPos.Y) );
+					}
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			SequenceAdapter data_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			return data_.SuggestXAxis();
+		}
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+			SequenceAdapter data_ = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			return data_.SuggestYAxis();
+		}
+
+
+		/// <summary>
+		/// Draws a representation of this plot in the legend.
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public void DrawInLegend( Graphics g, Rectangle startEnd )
+		{
+			if (marker_.Size > 0)
+            {
+                marker_.Draw(g, (startEnd.Left + startEnd.Right) / 2, (startEnd.Top + startEnd.Bottom) / 2);
+            }
+            else if (marker_.Pen.Width > 0)
+            {
+                g.DrawLine(marker_.Pen,
+                    (startEnd.Left + startEnd.Right) / 2, (startEnd.Top + startEnd.Bottom - marker_.Pen.Width) / 2,
+                    (startEnd.Left + startEnd.Right) / 2, (startEnd.Top + startEnd.Bottom + marker_.Pen.Width) / 2);
+            }
+		}
+
+
+		/// <summary>
+		/// The Marker object used for the plot.
+		/// </summary>
+		public Marker Marker
+		{
+			set
+			{
+				marker_ = value;
+			}
+			get
+			{
+				return marker_;
+			}
+		}
+
+
+
+	}
+}
\ No newline at end of file
diff --git a/nplot/nplot/PointPlot3D.cs b/nplot/nplot/PointPlot3D.cs
new file mode 100644
index 0000000..006e10c
--- /dev/null
+++ b/nplot/nplot/PointPlot3D.cs
@@ -0,0 +1,65 @@
+// ******** experimental ******** 
+
+/*
+using System;
+using System.Drawing;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Think we should just implement point plot first, unless you need
+	/// other stuff.
+	/// </summary>
+	public class PointPlot3D : BasePlot3D, IPlot3D
+	{
+		
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public PointPlot3D()
+		{
+
+		}
+
+		/// <summary>
+		/// Draws the points on the supplied axes. TODO.
+		/// </summary>
+		/// <param name="g"></param>
+		/// <param name="xAxis"></param>
+		/// <param name="yAxis"></param>
+		/// <param name="zAxis"></param>
+		void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis, PhysicalAxis zAxis )
+		{
+
+		}
+
+		/// <summary>
+		/// The method used to set the default x axis.
+		/// </summary>
+		public Axis SuggestXAxis()
+		{
+			return null;
+		}
+
+		/// <summary>
+		/// The method used to set the default y axis.
+		/// </summary>
+		public Axis SuggestYAxis()
+		{
+			return null;
+		}
+
+		/// <summary>
+		/// The method used to set the default z axis.
+		/// </summary>
+		public Axis SuggestZAxis()
+		{
+			return null;
+		}
+
+
+	}
+
+}
+*/
\ No newline at end of file
diff --git a/nplot/nplot/RectangleBrushes.cs b/nplot/nplot/RectangleBrushes.cs
new file mode 100644
index 0000000..0fe6eba
--- /dev/null
+++ b/nplot/nplot/RectangleBrushes.cs
@@ -0,0 +1,1953 @@
+/*
+NPlot - A charting library for .NET
+
+RectangleBrushes.cs
+Copyright (C) 2005
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Classes that implement this interface can provide a brush 
+	/// sized according to a given rectangle.
+	/// </summary>
+	public interface IRectangleBrush
+	{
+		/// <summary>
+		/// Gets a brush according to the supplied rectangle.
+		/// </summary>
+		/// <param name="rectangle">the rectangle used to construct the brush</param>
+		/// <returns>The brush</returns>
+		Brush Get( Rectangle rectangle );
+	}
+
+	/// <summary>
+	/// Collection of useful brushes.
+	/// </summary>
+	public class RectangleBrushes
+	{
+
+		/// <summary>
+		/// A solid brush
+		/// </summary>
+		public class Solid : IRectangleBrush
+		{
+			Brush brush_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="c">brush color</param>
+			public Solid( Color c )
+			{
+				brush_ = new SolidBrush( c );
+			}
+
+			/// <summary>
+			/// Gets a brush according to the supplied rectangle.
+			/// </summary>
+			/// <param name="rectangle">the rectangle used to construct the brush</param>
+			/// <returns>The solid brush</returns>
+			public Brush Get( Rectangle rectangle )
+			{
+				return brush_;
+			}
+
+			#region Default Brushes
+			/// <summary>
+			/// AliceBlue solid brush.
+			/// </summary>
+			public static Solid AliceBlue
+			{
+				get
+				{
+					return new Solid( Color.AliceBlue );
+				}
+			}
+
+			/// <summary>
+			/// AntiqueWhite solid brush.
+			/// </summary>
+			public static Solid AntiqueWhite
+			{
+				get
+				{
+					return new Solid( Color.AntiqueWhite );
+				}
+			}
+
+			/// <summary>
+			/// Aqua solid brush.
+			/// </summary>
+			public static Solid Aqua
+			{
+				get
+				{
+					return new Solid( Color.Aqua );
+				}
+			}
+
+			/// <summary>
+			/// Aquamarine solid brush.
+			/// </summary>
+			public static Solid Aquamarine
+			{
+				get
+				{
+					return new Solid( Color.Aquamarine );
+				}
+			}
+
+			/// <summary>
+			/// Azure solid brush.
+			/// </summary>
+			public static Solid Azure
+			{
+				get
+				{
+					return new Solid( Color.Azure );
+				}
+			}
+
+			/// <summary>
+			/// Beige solid brush.
+			/// </summary>
+			public static Solid Beige
+			{
+				get
+				{
+					return new Solid( Color.Beige );
+				}
+			}
+
+			/// <summary>
+			/// Bisque solid brush.
+			/// </summary>
+			public static Solid Bisque
+			{
+				get
+				{
+					return new Solid( Color.Bisque );
+				}
+			}
+
+			/// <summary>
+			/// Black solid brush.
+			/// </summary>
+			public static Solid Black
+			{
+				get
+				{
+					return new Solid( Color.Black );
+				}
+			}
+
+			/// <summary>
+			/// BlanchedAlmond solid brush.
+			/// </summary>
+			public static Solid BlanchedAlmond
+			{
+				get
+				{
+					return new Solid( Color.BlanchedAlmond );
+				}
+			}
+
+			/// <summary>
+			/// Blue solid brush.
+			/// </summary>
+			public static Solid Blue
+			{
+				get
+				{
+					return new Solid( Color.Blue );
+				}
+			}
+
+			/// <summary>
+			/// BlueViolet solid brush.
+			/// </summary>
+			public static Solid BlueViolet
+			{
+				get
+				{
+					return new Solid( Color.BlueViolet );
+				}
+			}
+
+			/// <summary>
+			/// Brown solid brush.
+			/// </summary>
+			public static Solid Brown
+			{
+				get
+				{
+					return new Solid( Color.Brown );
+				}
+			}
+
+			/// <summary>
+			/// BurlyWood solid brush.
+			/// </summary>
+			public static Solid BurlyWood
+			{
+				get
+				{
+					return new Solid( Color.BurlyWood );
+				}
+			}
+
+			/// <summary>
+			/// CadetBlue solid brush.
+			/// </summary>
+			public static Solid CadetBlue
+			{
+				get
+				{
+					return new Solid( Color.CadetBlue );
+				}
+			}
+
+			/// <summary>
+			/// Chartreuse solid brush.
+			/// </summary>
+			public static Solid Chartreuse
+			{
+				get
+				{
+					return new Solid( Color.Chartreuse );
+				}
+			}
+
+			/// <summary>
+			/// Chocolate solid brush.
+			/// </summary>
+			public static Solid Chocolate
+			{
+				get
+				{
+					return new Solid( Color.Chocolate );
+				}
+			}
+
+			/// <summary>
+			/// Coral solid brush.
+			/// </summary>
+			public static Solid Coral
+			{
+				get
+				{
+					return new Solid( Color.Coral );
+				}
+			}
+
+			/// <summary>
+			/// CornflowerBlue solid brush.
+			/// </summary>
+			public static Solid CornflowerBlue
+			{
+				get
+				{
+					return new Solid( Color.CornflowerBlue );
+				}
+			}
+
+			/// <summary>
+			/// Cornsilk solid brush.
+			/// </summary>
+			public static Solid Cornsilk
+			{
+				get
+				{
+					return new Solid( Color.Cornsilk );
+				}
+			}
+
+			/// <summary>
+			/// Crimson solid brush.
+			/// </summary>
+			public static Solid Crimson
+			{
+				get
+				{
+					return new Solid( Color.Crimson );
+				}
+			}
+
+			/// <summary>
+			/// Cyan solid brush.
+			/// </summary>
+			public static Solid Cyan
+			{
+				get
+				{
+					return new Solid( Color.Cyan );
+				}
+			}
+
+			/// <summary>
+			/// DarkBlue solid brush.
+			/// </summary>
+			public static Solid DarkBlue
+			{
+				get
+				{
+					return new Solid( Color.DarkBlue );
+				}
+			}
+
+			/// <summary>
+			/// DarkCyan solid brush.
+			/// </summary>
+			public static Solid DarkCyan
+			{
+				get
+				{
+					return new Solid( Color.DarkCyan );
+				}
+			}
+
+			/// <summary>
+			/// DarkGoldenrod solid brush.
+			/// </summary>
+			public static Solid DarkGoldenrod
+			{
+				get
+				{
+					return new Solid( Color.DarkGoldenrod );
+				}
+			}
+
+			/// <summary>
+			/// DarkGray solid brush.
+			/// </summary>
+			public static Solid DarkGray
+			{
+				get
+				{
+					return new Solid( Color.DarkGray );
+				}
+			}
+
+			/// <summary>
+			/// DarkGreen solid brush.
+			/// </summary>
+			public static Solid DarkGreen
+			{
+				get
+				{
+					return new Solid( Color.DarkGreen );
+				}
+			}
+
+			/// <summary>
+			/// DarkKhaki solid brush.
+			/// </summary>
+			public static Solid DarkKhaki
+			{
+				get
+				{
+					return new Solid( Color.DarkKhaki );
+				}
+			}
+
+			/// <summary>
+			/// DarkMagenta solid brush.
+			/// </summary>
+			public static Solid DarkMagenta
+			{
+				get
+				{
+					return new Solid( Color.DarkMagenta );
+				}
+			}
+
+			/// <summary>
+			/// DarkOliveGreen solid brush.
+			/// </summary>
+			public static Solid DarkOliveGreen
+			{
+				get
+				{
+					return new Solid( Color.DarkOliveGreen );
+				}
+			}
+
+			/// <summary>
+			/// DarkOrange solid brush.
+			/// </summary>
+			public static Solid DarkOrange
+			{
+				get
+				{
+					return new Solid( Color.DarkOrange );
+				}
+			}
+
+			/// <summary>
+			/// DarkOrchid solid brush.
+			/// </summary>
+			public static Solid DarkOrchid
+			{
+				get
+				{
+					return new Solid( Color.DarkOrchid );
+				}
+			}
+
+			/// <summary>
+			/// DarkRed solid brush.
+			/// </summary>
+			public static Solid DarkRed
+			{
+				get
+				{
+					return new Solid( Color.DarkRed );
+				}
+			}
+
+			/// <summary>
+			/// DarkSalmon solid brush.
+			/// </summary>
+			public static Solid DarkSalmon
+			{
+				get
+				{
+					return new Solid( Color.DarkSalmon );
+				}
+			}
+
+			/// <summary>
+			/// DarkSeaGreen solid brush.
+			/// </summary>
+			public static Solid DarkSeaGreen
+			{
+				get
+				{
+					return new Solid( Color.DarkSeaGreen );
+				}
+			}
+
+			/// <summary>
+			/// DarkSlateBlue solid brush.
+			/// </summary>
+			public static Solid DarkSlateBlue
+			{
+				get
+				{
+					return new Solid( Color.DarkSlateBlue );
+				}
+			}
+
+			/// <summary>
+			/// DarkSlateGray solid brush.
+			/// </summary>
+			public static Solid DarkSlateGray
+			{
+				get
+				{
+					return new Solid( Color.DarkSlateGray );
+				}
+			}
+
+			/// <summary>
+			/// DarkTurquoise solid brush.
+			/// </summary>
+			public static Solid DarkTurquoise
+			{
+				get
+				{
+					return new Solid( Color.DarkTurquoise );
+				}
+			}
+
+			/// <summary>
+			/// DarkViolet solid brush.
+			/// </summary>
+			public static Solid DarkViolet
+			{
+				get
+				{
+					return new Solid( Color.DarkViolet );
+				}
+			}
+
+			/// <summary>
+			/// DeepPink solid brush.
+			/// </summary>
+			public static Solid DeepPink
+			{
+				get
+				{
+					return new Solid( Color.DeepPink );
+				}
+			}
+
+			/// <summary>
+			/// DeepSkyBlue solid brush.
+			/// </summary>
+			public static Solid DeepSkyBlue
+			{
+				get
+				{
+					return new Solid( Color.DeepSkyBlue );
+				}
+			}
+
+			/// <summary>
+			/// DimGray solid brush.
+			/// </summary>
+			public static Solid DimGray
+			{
+				get
+				{
+					return new Solid( Color.DimGray );
+				}
+			}
+
+			/// <summary>
+			/// DodgerBlue solid brush.
+			/// </summary>
+			public static Solid DodgerBlue
+			{
+				get
+				{
+					return new Solid( Color.DodgerBlue );
+				}
+			}
+
+			/// <summary>
+			/// Firebrick solid brush.
+			/// </summary>
+			public static Solid Firebrick
+			{
+				get
+				{
+					return new Solid( Color.Firebrick );
+				}
+			}
+
+			/// <summary>
+			/// FloralWhite solid brush.
+			/// </summary>
+			public static Solid FloralWhite
+			{
+				get
+				{
+					return new Solid( Color.FloralWhite );
+				}
+			}
+
+			/// <summary>
+			/// ForestGreen solid brush.
+			/// </summary>
+			public static Solid ForestGreen
+			{
+				get
+				{
+					return new Solid( Color.ForestGreen );
+				}
+			}
+
+			/// <summary>
+			/// Fuchsia solid brush.
+			/// </summary>
+			public static Solid Fuchsia
+			{
+				get
+				{
+					return new Solid( Color.Fuchsia );
+				}
+			}
+
+			/// <summary>
+			/// Gainsboro solid brush.
+			/// </summary>
+			public static Solid Gainsboro
+			{
+				get
+				{
+					return new Solid( Color.Gainsboro );
+				}
+			}
+
+			/// <summary>
+			/// GhostWhite solid brush.
+			/// </summary>
+			public static Solid GhostWhite
+			{
+				get
+				{
+					return new Solid( Color.GhostWhite );
+				}
+			}
+
+			/// <summary>
+			/// Gold solid brush.
+			/// </summary>
+			public static Solid Gold
+			{
+				get
+				{
+					return new Solid( Color.Gold );
+				}
+			}
+
+			/// <summary>
+			/// Goldenrod solid brush.
+			/// </summary>
+			public static Solid Goldenrod
+			{
+				get
+				{
+					return new Solid( Color.Goldenrod );
+				}
+			}
+
+			/// <summary>
+			/// Gray  solid brush.
+			/// </summary>
+			public static Solid Gray 
+			{
+				get
+				{
+					return new Solid( Color.Gray );
+				}
+			}
+
+			/// <summary>
+			/// Green solid brush.
+			/// </summary>
+			public static Solid Green
+			{
+				get
+				{
+					return new Solid( Color.Green );
+				}
+			}
+
+			/// <summary>
+			/// GreenYellow solid brush.
+			/// </summary>
+			public static Solid GreenYellow
+			{
+				get
+				{
+					return new Solid( Color.GreenYellow );
+				}
+			}
+
+			/// <summary>
+			/// Honeydew solid brush.
+			/// </summary>
+			public static Solid Honeydew
+			{
+				get
+				{
+					return new Solid( Color.Honeydew );
+				}
+			}
+
+			/// <summary>
+			/// HotPink solid brush.
+			/// </summary>
+			public static Solid HotPink
+			{
+				get
+				{
+					return new Solid( Color.HotPink );
+				}
+			}
+
+			/// <summary>
+			/// IndianRed solid brush.
+			/// </summary>
+			public static Solid IndianRed
+			{
+				get
+				{
+					return new Solid( Color.IndianRed );
+				}
+			}
+
+			/// <summary>
+			/// Indigo solid brush.
+			/// </summary>
+			public static Solid Indigo
+			{
+				get
+				{
+					return new Solid( Color.Indigo );
+				}
+			}
+
+			/// <summary>
+			/// Ivory solid brush.
+			/// </summary>
+			public static Solid Ivory
+			{
+				get
+				{
+					return new Solid( Color.Ivory );
+				}
+			}
+
+			/// <summary>
+			/// Khaki solid brush.
+			/// </summary>
+			public static Solid Khaki
+			{
+				get
+				{
+					return new Solid( Color.Khaki );
+				}
+			}
+
+			/// <summary>
+			/// Lavender solid brush.
+			/// </summary>
+			public static Solid Lavender
+			{
+				get
+				{
+					return new Solid( Color.Lavender );
+				}
+			}
+
+			/// <summary>
+			/// LavenderBlush solid brush.
+			/// </summary>
+			public static Solid LavenderBlush
+			{
+				get
+				{
+					return new Solid( Color.LavenderBlush );
+				}
+			}
+
+			/// <summary>
+			/// LawnGreen solid brush.
+			/// </summary>
+			public static Solid LawnGreen
+			{
+				get
+				{
+					return new Solid( Color.LawnGreen );
+				}
+			}
+
+			/// <summary>
+			/// LemonChiffon solid brush.
+			/// </summary>
+			public static Solid LemonChiffon
+			{
+				get
+				{
+					return new Solid( Color.LemonChiffon );
+				}
+			}
+
+			/// <summary>
+			/// LightBlue solid brush.
+			/// </summary>
+			public static Solid LightBlue
+			{
+				get
+				{
+					return new Solid( Color.LightBlue );
+				}
+			}
+
+			/// <summary>
+			/// LightCoral solid brush.
+			/// </summary>
+			public static Solid LightCoral
+			{
+				get
+				{
+					return new Solid( Color.LightCoral );
+				}
+			}
+
+			/// <summary>
+			/// LightCyan solid brush.
+			/// </summary>
+			public static Solid LightCyan
+			{
+				get
+				{
+					return new Solid( Color.LightCyan );
+				}
+			}
+
+			/// <summary>
+			/// LightGoldenrodYellow solid brush.
+			/// </summary>
+			public static Solid LightGoldenrodYellow
+			{
+				get
+				{
+					return new Solid( Color.LightGoldenrodYellow );
+				}
+			}
+
+			/// <summary>
+			/// LightGray solid brush.
+			/// </summary>
+			public static Solid LightGray
+			{
+				get
+				{
+					return new Solid( Color.LightGray );
+				}
+			}
+
+			/// <summary>
+			/// LightGreen solid brush.
+			/// </summary>
+			public static Solid LightGreen
+			{
+				get
+				{
+					return new Solid( Color.LightGreen );
+				}
+			}
+
+			/// <summary>
+			/// LightPink solid brush.
+			/// </summary>
+			public static Solid LightPink
+			{
+				get
+				{
+					return new Solid( Color.LightPink );
+				}
+			}
+
+			/// <summary>
+			/// LightSalmon solid brush.
+			/// </summary>
+			public static Solid LightSalmon
+			{
+				get
+				{
+					return new Solid( Color.LightSalmon );
+				}
+			}
+
+			/// <summary>
+			/// LightSeaGreen solid brush.
+			/// </summary>
+			public static Solid LightSeaGreen
+			{
+				get
+				{
+					return new Solid( Color.LightSeaGreen );
+				}
+			}
+
+			/// <summary>
+			/// LightSkyBlue solid brush.
+			/// </summary>
+			public static Solid LightSkyBlue
+			{
+				get
+				{
+					return new Solid( Color.LightSkyBlue );
+				}
+			}
+
+			/// <summary>
+			/// LightSlateGray solid brush.
+			/// </summary>
+			public static Solid LightSlateGray
+			{
+				get
+				{
+					return new Solid( Color.LightSlateGray );
+				}
+			}
+
+			/// <summary>
+			/// LightSteelBlue solid brush.
+			/// </summary>
+			public static Solid LightSteelBlue
+			{
+				get
+				{
+					return new Solid( Color.LightSteelBlue );
+				}
+			}
+
+			/// <summary>
+			/// LightYellow solid brush.
+			/// </summary>
+			public static Solid LightYellow
+			{
+				get
+				{
+					return new Solid( Color.LightYellow );
+				}
+			}
+
+			/// <summary>
+			/// Lime solid brush.
+			/// </summary>
+			public static Solid Lime
+			{
+				get
+				{
+					return new Solid( Color.Lime );
+				}
+			}
+
+			/// <summary>
+			/// LimeGreen solid brush.
+			/// </summary>
+			public static Solid LimeGreen
+			{
+				get
+				{
+					return new Solid( Color.LimeGreen );
+				}
+			}
+
+			/// <summary>
+			/// Color.Linen solid brush.
+			/// </summary>
+			public static Solid Linen
+			{
+				get
+				{
+					return new Solid( Color.Linen );
+				}
+			}
+
+			/// <summary>
+			/// Color.Magenta solid brush.
+			/// </summary>
+			public static Solid Magenta
+			{
+				get
+				{
+					return new Solid( Color.Magenta );
+				}
+			}
+
+			/// <summary>
+			/// Maroon solid brush.
+			/// </summary>
+			public static Solid Maroon
+			{
+				get
+				{
+					return new Solid( Color.Maroon );
+				}
+			}
+
+			/// <summary>
+			/// MediumAquamarine solid brush.
+			/// </summary>
+			public static Solid MediumAquamarine
+			{
+				get
+				{
+					return new Solid( Color.MediumAquamarine );
+				}
+			}
+
+			/// <summary>
+			/// MediumBlue solid brush.
+			/// </summary>
+			public static Solid MediumBlue
+			{
+				get
+				{
+					return new Solid( Color.MediumBlue );
+				}
+			}
+
+			/// <summary>
+			/// MediumOrchid  solid brush.
+			/// </summary>
+			public static Solid MediumOrchid 
+			{
+				get
+				{
+					return new Solid( Color.MediumOrchid );
+				}
+			}
+
+			/// <summary>
+			/// MediumPurple solid brush.
+			/// </summary>
+			public static Solid MediumPurple
+			{
+				get
+				{
+					return new Solid( Color.MediumPurple );
+				}
+			}
+
+			/// <summary>
+			/// MediumSeaGreen solid brush.
+			/// </summary>
+			public static Solid MediumSeaGreen
+			{
+				get
+				{
+					return new Solid( Color.MediumSeaGreen );
+				}
+			}
+
+			/// <summary>
+			/// MediumSlateBlue solid brush.
+			/// </summary>
+			public static Solid MediumSlateBlue
+			{
+				get
+				{
+					return new Solid( Color.MediumSlateBlue );
+				}
+			}
+
+			/// <summary>
+			/// MediumSpringGreen solid brush.
+			/// </summary>
+			public static Solid MediumSpringGreen
+			{
+				get
+				{
+					return new Solid( Color.MediumSpringGreen );
+				}
+			}
+
+			/// <summary>
+			/// MediumTurquoise solid brush.
+			/// </summary>
+			public static Solid MediumTurquoise
+			{
+				get
+				{
+					return new Solid( Color.MediumTurquoise );
+				}
+			}
+
+			/// <summary>
+			/// MediumVioletRed solid brush.
+			/// </summary>
+			public static Solid MediumVioletRed
+			{
+				get
+				{
+					return new Solid( Color.MediumVioletRed );
+				}
+			}
+
+			/// <summary>
+			/// MidnightBlue  solid brush.
+			/// </summary>
+			public static Solid MidnightBlue 
+			{
+				get
+				{
+					return new Solid( Color.MidnightBlue );
+				}
+			}
+
+			/// <summary>
+			/// MintCream solid brush.
+			/// </summary>
+			public static Solid MintCream
+			{
+				get
+				{
+					return new Solid( Color.MintCream );
+				}
+			}
+
+			/// <summary>
+			/// MistyRose solid brush.
+			/// </summary>
+			public static Solid MistyRose
+			{
+				get
+				{
+					return new Solid( Color.MistyRose );
+				}
+			}
+
+			/// <summary>
+			/// Moccasin solid brush.
+			/// </summary>
+			public static Solid Moccasin
+			{
+				get
+				{
+					return new Solid( Color.Moccasin );
+				}
+			}
+
+			/// <summary>
+			/// NavajoWhite solid brush.
+			/// </summary>
+			public static Solid NavajoWhite
+			{
+				get
+				{
+					return new Solid( Color.NavajoWhite );
+				}
+			}
+
+			/// <summary>
+			/// Navy solid brush.
+			/// </summary>
+			public static Solid Navy
+			{
+				get
+				{
+					return new Solid( Color.Navy );
+				}
+			}
+
+			/// <summary>
+			/// OldLace solid brush.
+			/// </summary>
+			public static Solid OldLace
+			{
+				get
+				{
+					return new Solid( Color.OldLace );
+				}
+			}
+
+			/// <summary>
+			/// Olive solid brush.
+			/// </summary>
+			public static Solid Olive
+			{
+				get
+				{
+					return new Solid( Color.Olive );
+				}
+			}
+
+			/// <summary>
+			/// OliveDrab solid brush.
+			/// </summary>
+			public static Solid OliveDrab
+			{
+				get
+				{
+					return new Solid( Color.OliveDrab );
+				}
+			}
+
+			/// <summary>
+			/// Orange solid brush.
+			/// </summary>
+			public static Solid Orange
+			{
+				get
+				{
+					return new Solid( Color.Orange );
+				}
+			}
+
+			/// <summary>
+			/// OrangeRed solid brush.
+			/// </summary>
+			public static Solid OrangeRed
+			{
+				get
+				{
+					return new Solid( Color.OrangeRed );
+				}
+			}
+
+			/// <summary>
+			/// Orchid solid brush.
+			/// </summary>
+			public static Solid Orchid
+			{
+				get
+				{
+					return new Solid( Color.Orchid );
+				}
+			}
+
+			/// <summary>
+			/// PaleGoldenrod solid brush.
+			/// </summary>
+			public static Solid PaleGoldenrod
+			{
+				get
+				{
+					return new Solid( Color.PaleGoldenrod );
+				}
+			}
+
+			/// <summary>
+			/// PaleGreen solid brush.
+			/// </summary>
+			public static Solid PaleGreen
+			{
+				get
+				{
+					return new Solid( Color.PaleGreen );
+				}
+			}
+
+			/// <summary>
+			/// PaleTurquoise solid brush.
+			/// </summary>
+			public static Solid PaleTurquoise
+			{
+				get
+				{
+					return new Solid( Color.PaleTurquoise );
+				}
+			}
+
+			/// <summary>
+			/// PaleVioletRed solid brush.
+			/// </summary>
+			public static Solid PaleVioletRed
+			{
+				get
+				{
+					return new Solid( Color.PaleVioletRed );
+				}
+			}
+
+			/// <summary>
+			/// PapayaWhip solid brush.
+			/// </summary>
+			public static Solid PapayaWhip
+			{
+				get
+				{
+					return new Solid( Color.PapayaWhip );
+				}
+			}
+
+			/// <summary>
+			/// PeachPuff solid brush.
+			/// </summary>
+			public static Solid PeachPuff
+			{
+				get
+				{
+					return new Solid( Color.PeachPuff );
+				}
+			}
+
+			/// <summary>
+			/// Peru solid brush.
+			/// </summary>
+			public static Solid Peru
+			{
+				get
+				{
+					return new Solid( Color.Peru );
+				}
+			}
+
+			/// <summary>
+			/// Pink solid brush.
+			/// </summary>
+			public static Solid Pink
+			{
+				get
+				{
+					return new Solid( Color.Pink );
+				}
+			}
+
+			/// <summary>
+			/// Plum solid brush.
+			/// </summary>
+			public static Solid Plum
+			{
+				get
+				{
+					return new Solid( Color.Plum );
+				}
+			}
+
+			/// <summary>
+			/// PowderBlue solid brush.
+			/// </summary>
+			public static Solid PowderBlue
+			{
+				get
+				{
+					return new Solid( Color.PowderBlue );
+				}
+			}
+
+			/// <summary>
+			/// Purple solid brush.
+			/// </summary>
+			public static Solid Purple
+			{
+				get
+				{
+					return new Solid( Color.Purple );
+				}
+			}
+
+			/// <summary>
+			/// Red solid brush.
+			/// </summary>
+			public static Solid Red
+			{
+				get
+				{
+					return new Solid( Color.Red );
+				}
+			}
+
+			/// <summary>
+			/// RosyBrown solid brush.
+			/// </summary>
+			public static Solid RosyBrown
+			{
+				get
+				{
+					return new Solid( Color.RosyBrown );
+				}
+			}
+
+			/// <summary>
+			/// RoyalBlue solid brush.
+			/// </summary>
+			public static Solid RoyalBlue
+			{
+				get
+				{
+					return new Solid( Color.RoyalBlue );
+				}
+			}
+
+			/// <summary>
+			/// SaddleBrown solid brush.
+			/// </summary>
+			public static Solid SaddleBrown
+			{
+				get
+				{
+					return new Solid( Color.SaddleBrown );
+				}
+			}
+
+			/// <summary>
+			/// Salmon solid brush.
+			/// </summary>
+			public static Solid Salmon
+			{
+				get
+				{
+					return new Solid( Color.Salmon );
+				}
+			}
+
+			/// <summary>
+			/// SandyBrown solid brush.
+			/// </summary>
+			public static Solid SandyBrown
+			{
+				get
+				{
+					return new Solid( Color.SandyBrown );
+				}
+			}
+
+			/// <summary>
+			/// SeaGreen solid brush.
+			/// </summary>
+			public static Solid SeaGreen
+			{
+				get
+				{
+					return new Solid( Color.SeaGreen );
+				}
+			}
+
+			/// <summary>
+			/// SeaShell solid brush.
+			/// </summary>
+			public static Solid SeaShell
+			{
+				get
+				{
+					return new Solid( Color.SeaShell );
+				}
+			}
+
+			/// <summary>
+			/// Sienna solid brush.
+			/// </summary>
+			public static Solid Sienna
+			{
+				get
+				{
+					return new Solid( Color.Sienna );
+				}
+			}
+
+			/// <summary>
+			/// Silver solid brush.
+			/// </summary>
+			public static Solid Silver
+			{
+				get
+				{
+					return new Solid( Color.Silver );
+				}
+			}
+
+			/// <summary>
+			/// SkyBlue solid brush.
+			/// </summary>
+			public static Solid SkyBlue
+			{
+				get
+				{
+					return new Solid( Color.SkyBlue );
+				}
+			}
+
+			/// <summary>
+			/// SlateBlue solid brush.
+			/// </summary>
+			public static Solid SlateBlue
+			{
+				get
+				{
+					return new Solid( Color.SlateBlue );
+				}
+			}
+
+			/// <summary>
+			/// SlateGray solid brush.
+			/// </summary>
+			public static Solid SlateGray
+			{
+				get
+				{
+					return new Solid( Color.SlateGray );
+				}
+			}
+
+			/// <summary>
+			/// Snow solid brush.
+			/// </summary>
+			public static Solid Snow
+			{
+				get
+				{
+					return new Solid( Color.Snow );
+				}
+			}
+
+			/// <summary>
+			/// SpringGreen solid brush.
+			/// </summary>
+			public static Solid SpringGreen
+			{
+				get
+				{
+					return new Solid( Color.SpringGreen );
+				}
+			}
+
+			/// <summary>
+			/// SteelBlue solid brush.
+			/// </summary>
+			public static Solid SteelBlue
+			{
+				get
+				{
+					return new Solid( Color.SteelBlue );
+				}
+			}
+
+			/// <summary>
+			/// Tan solid brush.
+			/// </summary>
+			public static Solid Tan
+			{
+				get
+				{
+					return new Solid( Color.Tan );
+				}
+			}
+
+			/// <summary>
+			/// Teal solid brush.
+			/// </summary>
+			public static Solid Teal
+			{
+				get
+				{
+					return new Solid( Color.Teal );
+				}
+			}
+
+			/// <summary>
+			/// Thistle solid brush.
+			/// </summary>
+			public static Solid Thistle
+			{
+				get
+				{
+					return new Solid( Color.Thistle );
+				}
+			}
+
+			/// <summary>
+			/// Tomato solid brush.
+			/// </summary>
+			public static Solid Tomato
+			{
+				get
+				{
+					return new Solid( Color.Tomato );
+				}
+			}
+
+			/// <summary>
+			/// Transparent solid brush.
+			/// </summary>
+			public static Solid Transparent
+			{
+				get
+				{
+					return new Solid( Color.Transparent );
+				}
+			}
+
+			/// <summary>
+			/// Turquoise solid brush.
+			/// </summary>
+			public static Solid Turquoise
+			{
+				get
+				{
+					return new Solid( Color.Turquoise );
+				}
+			}
+
+			/// <summary>
+			/// Violet solid brush.
+			/// </summary>
+			public static Solid Violet
+			{
+				get
+				{
+					return new Solid( Color.Violet );
+				}
+			}
+
+			/// <summary>
+			/// Wheat solid brush.
+			/// </summary>
+			public static Solid Wheat
+			{
+				get
+				{
+					return new Solid( Color.Wheat );
+				}
+			}
+
+			/// <summary>
+			/// White solid brush.
+			/// </summary>
+			public static Solid White
+			{
+				get
+				{
+					return new Solid( Color.White );
+				}
+			}
+
+			/// <summary>
+			/// WhiteSmoke solid brush.
+			/// </summary>
+			public static Solid WhiteSmoke
+			{
+				get
+				{
+					return new Solid( Color.WhiteSmoke );
+				}
+			}
+
+			/// <summary>
+			/// Yellow solid brush.
+			/// </summary>
+			public static Solid Yellow
+			{
+				get
+				{
+					return new Solid( Color.Yellow );
+				}
+			}
+
+			/// <summary>
+			/// YellowGreen solid brush.
+			/// </summary>
+			public static Solid YellowGreen
+			{
+				get
+				{
+					return new Solid( Color.YellowGreen );
+				}
+			}
+
+			#endregion
+
+		}
+
+
+		/// <summary>
+		/// A brush with horizontal gradient.
+		/// </summary>
+		public class Horizontal : IRectangleBrush
+		{
+			private Color c1_;
+			private Color c2_;
+			
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="c1">Color on left.</param>
+			/// <param name="c2">Color on right.</param>
+			public Horizontal( Color c1, Color c2 )
+			{
+				c1_ = c1;
+				c2_ = c2;
+			}
+
+			/// <summary>
+			/// Gets a brush according to the supplied rectangle.
+			/// </summary>
+			/// <param name="rectangle">the rectangle used to construct the brush</param>
+			/// <returns>The horizontal brush</returns>
+			public Brush Get( Rectangle rectangle )
+			{
+				return new LinearGradientBrush( rectangle, c1_, c2_, LinearGradientMode.Horizontal );
+			}
+
+			#region DefaultBrushes
+
+			/// <summary>
+			/// Default brush - fades from faint blue to white.
+			/// </summary>
+			public static Horizontal FaintBlueFade
+			{
+				get
+				{
+					return new Horizontal( Color.FromArgb(200,200,255), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static Horizontal FaintRedFade
+			{
+				get
+				{
+					return new Horizontal( Color.FromArgb(255,200,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static Horizontal FaintGreenFade
+			{
+				get
+				{
+					return new Horizontal( Color.FromArgb(200,255,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			#endregion
+
+		}
+
+
+		/// <summary>
+		/// A brush with vertical gradient.
+		/// </summary>
+		public class Vertical : IRectangleBrush
+		{
+			private Color c1_;
+			private Color c2_;
+			
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="c1">top color [or bottom?]</param>
+			/// <param name="c2">bottom color [or top?]</param>
+			public Vertical( Color c1, Color c2 )
+			{
+				c1_ = c1;
+				c2_ = c2;
+			}
+
+			/// <summary>
+			/// Gets a brush according to the supplied rectangle.
+			/// </summary>
+			/// <param name="rectangle">the rectangle used to construct the brush</param>
+			/// <returns>The vertical brush</returns>
+			public Brush Get( Rectangle rectangle )
+			{
+				return new LinearGradientBrush( rectangle, c1_, c2_, LinearGradientMode.Vertical );
+			}
+
+
+			#region DefaultBrushes
+
+			/// <summary>
+			/// Default brush - fades from faint blue to white.
+			/// </summary>
+			public static Vertical FaintBlueFade
+			{
+				get
+				{
+					return new Vertical( Color.FromArgb(200,200,255), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static Vertical FaintRedFade
+			{
+				get
+				{
+					return new Vertical( Color.FromArgb(255,200,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static Vertical FaintGreenFade
+			{
+				get
+				{
+					return new Vertical( Color.FromArgb(200,255,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			#endregion
+		}
+
+
+		/// <summary>
+		/// A brush with horizontal gradient that fades into center then out again.
+		/// </summary>
+		public class HorizontalCenterFade : IRectangleBrush
+		{
+			private Color c1_;
+			private Color c2_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="c1">inner color</param>
+			/// <param name="c2">outer color</param>
+			public HorizontalCenterFade( Color c1, Color c2 )
+			{
+				c1_ = c1;
+				c2_ = c2;
+			}
+
+			/// <summary>
+			/// Gets a brush according to the supplied rectangle.
+			/// </summary>
+			/// <param name="rectangle">the rectangle used to construct the brush</param>
+			/// <returns>The horizontal center fade brush</returns>
+			public Brush Get( Rectangle rectangle )
+			{
+				LinearGradientBrush brush = new LinearGradientBrush( rectangle, c1_, c2_, LinearGradientMode.Horizontal );
+				float[] relativeIntensities = { 0.0f, 0.9f, 1.0f, 0.9f, 0.0f };
+				float[] relativePositions   = { 0.0f, 0.4f, 0.5f, 0.6f, 1.0f };
+				Blend blend = new Blend();
+				blend.Factors = relativeIntensities;
+				blend.Positions = relativePositions;
+				brush.Blend = blend;
+				return brush;
+			}
+
+			#region DefaultBrushes
+
+			/// <summary>
+			/// Default brush - fades from faint blue to white.
+			/// </summary>
+			public static HorizontalCenterFade FaintBlueFade
+			{
+				get
+				{
+					return new HorizontalCenterFade( Color.FromArgb(200,200,255), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static HorizontalCenterFade FaintRedFade
+			{
+				get
+				{
+					return new HorizontalCenterFade( Color.FromArgb(255,200,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static HorizontalCenterFade FaintGreenFade
+			{
+				get
+				{
+					return new HorizontalCenterFade( Color.FromArgb(200,255,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			#endregion
+		}
+
+
+
+		/// <summary>
+		/// Brush with vertical gradient that fades into center then out again.
+		/// </summary>
+		public class VerticalCenterFade : IRectangleBrush
+		{
+			private Color c1_;
+			private Color c2_;
+
+			/// <summary>
+			/// Constructor
+			/// </summary>
+			/// <param name="c1">inner color</param>
+			/// <param name="c2">outer color</param>
+			public VerticalCenterFade( Color c1, Color c2 )
+			{
+				c1_ = c1;
+				c2_ = c2;
+			}
+
+			/// <summary>
+			/// Gets a brush according to the supplied rectangle.
+			/// </summary>
+			/// <param name="rectangle">the rectangle used to construct the brush</param>
+			/// <returns>The vertical center fade brush</returns>
+			public Brush Get( Rectangle rectangle )
+			{
+				LinearGradientBrush brush = new LinearGradientBrush( rectangle, c1_, c2_, LinearGradientMode.Vertical );
+				float[] relativeIntensities = { 0.0f, 0.9f, 1.0f, 0.9f, 0.0f };
+				float[] relativePositions   = { 0.0f, 0.4f, 0.5f, 0.6f, 1.0f };
+				Blend blend = new Blend();
+				blend.Factors = relativeIntensities;
+				blend.Positions = relativePositions;
+				brush.Blend = blend;
+				return brush;
+			}
+
+			#region DefaultBrushes
+
+			/// <summary>
+			/// Default brush - fades from faint blue to white.
+			/// </summary>
+			public static VerticalCenterFade FaintBlueFade
+			{
+				get
+				{
+					return new VerticalCenterFade( Color.FromArgb(200,200,255), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static VerticalCenterFade FaintRedFade
+			{
+				get
+				{
+					return new VerticalCenterFade( Color.FromArgb(255,200,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			/// <summary>
+			/// Default brush - fades from faint red to white.
+			/// </summary>
+			public static VerticalCenterFade FaintGreenFade
+			{
+				get
+				{
+					return new VerticalCenterFade( Color.FromArgb(200,255,200), Color.FromArgb(255,255,255) );
+				}
+			}
+
+			#endregion
+
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/RectangleD.cs b/nplot/nplot/RectangleD.cs
new file mode 100644
index 0000000..385ab03
--- /dev/null
+++ b/nplot/nplot/RectangleD.cs
@@ -0,0 +1,143 @@
+/*
+NPlot - A charting library for .NET
+
+RectangleD.cs
+Copyright (C) 2005
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// Stores a set of four double numbers that represent the location and size of
+	/// a rectangle. TODO: implement more functionality similar to Drawing.RectangleF.
+	/// </summary>
+	public struct RectangleD
+	{
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public RectangleD( double x, double y, double width, double height )
+		{
+			x_ = x;
+			y_ = y;
+			width_ = width;
+			height_ = height;
+		}
+
+		/// <summary>
+		/// The rectangle height.
+		/// </summary>
+		public double Height
+		{
+			get
+			{
+				return height_;
+			}
+			set
+			{
+				height_ = value;
+			}
+		}
+
+		/// <summary>
+		/// The rectangle width.
+		/// </summary>
+		public double Width
+		{
+			get
+			{
+				return width_;
+			}
+			set
+			{
+				width_ = value;
+			}
+		}
+
+		/// <summary>
+		/// The minimum x coordinate of the rectangle.
+		/// </summary>
+		public double X
+		{
+			get
+			{
+				return x_;
+			}
+			set
+			{
+				x_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The minimum y coordinate of the rectangle.
+		/// </summary>
+		public double Y
+		{
+			get
+			{
+				return y_;
+			}
+			set
+			{
+				y_ = value;
+			}
+		}
+
+
+		private double x_;
+		private double y_;
+		private double width_;
+		private double height_;
+
+	}
+}
diff --git a/nplot/nplot/SequenceAdapter.cs b/nplot/nplot/SequenceAdapter.cs
new file mode 100644
index 0000000..cfaba9f
--- /dev/null
+++ b/nplot/nplot/SequenceAdapter.cs
@@ -0,0 +1,330 @@
+/*
+NPlot - A charting library for .NET
+
+SequenceAdapter.cs
+Copyright (C) 2003-2004
+Matt Howlett 
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
+using System.Data;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// This class is responsible for interpreting the various ways you can 
+	/// specify data to plot objects using the DataSource, DataMember, ordinateData
+	/// and AbscissaData properties. It is a bridge that provides access to this
+	/// data via a single interface.
+	/// </summary>
+	public class SequenceAdapter
+	{
+		private AdapterUtils.IAxisSuggester XAxisSuggester_;
+		private AdapterUtils.IAxisSuggester YAxisSuggester_;
+        private AdapterUtils.ICounter counter_;
+        private AdapterUtils.IDataGetter xDataGetter_;
+        private AdapterUtils.IDataGetter yDataGetter_;
+
+        /// <summary>
+		/// Constructor. The data source specifiers must be specified here.
+		/// </summary>
+		/// <param name="dataSource">The source containing a list of values to plot.</param>
+		/// <param name="dataMember">The specific data member in a multimember data source to get data from.</param>
+		/// <param name="ordinateData">The source containing a list of values to plot on the ordinate axis, or a the name of the column to use for this data.</param>
+		/// <param name="abscissaData">The source containing a list of values to plot on the abscissa axis, or a the name of the column to use for this data.</param>
+		public SequenceAdapter( object dataSource, string dataMember, object ordinateData, object abscissaData )
+		{
+
+			if (dataSource == null && dataMember == null)
+			{
+				if (ordinateData is IList) 
+				{
+
+                    this.YAxisSuggester_ = new AdapterUtils.AxisSuggester_IList((IList)ordinateData);
+                    if (ordinateData is Double[])
+                        this.yDataGetter_ = new AdapterUtils.DataGetter_DoublesArray((Double[])ordinateData);
+                    else
+                        this.yDataGetter_ = new AdapterUtils.DataGetter_IList((IList)ordinateData);
+
+                    this.counter_ = new AdapterUtils.Counter_IList((IList)ordinateData);
+
+                    if (abscissaData is IList)
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_IList((IList)abscissaData);
+                        if (abscissaData is Double[])
+                            this.xDataGetter_ = new AdapterUtils.DataGetter_DoublesArray((Double[])abscissaData);
+                        else
+                            this.xDataGetter_ = new AdapterUtils.DataGetter_IList((IList)abscissaData);
+
+                        return;
+					}
+
+					else if (abscissaData is StartStep)
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_StartStep((StartStep)abscissaData, (IList)ordinateData);
+                        this.xDataGetter_ = new AdapterUtils.DataGetter_StartStep((StartStep)abscissaData);
+                        return;
+					}
+
+					else if (abscissaData == null)
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_Auto((IList)ordinateData);
+                        this.xDataGetter_ = new AdapterUtils.DataGetter_Count();
+                        return;
+					}
+				}
+				
+
+				else if (ordinateData == null)
+				{
+					if (abscissaData == null)
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_Null();
+                        this.YAxisSuggester_ = new AdapterUtils.AxisSuggester_Null();
+                        this.counter_ = new AdapterUtils.Counter_Null();
+                        this.xDataGetter_ = new AdapterUtils.DataGetter_Null();
+                        this.yDataGetter_ = new AdapterUtils.DataGetter_Null();
+                        return;
+					}
+					else if (abscissaData is IList)
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_IList((IList)abscissaData);
+                        this.YAxisSuggester_ = new AdapterUtils.AxisSuggester_Auto((IList)abscissaData);
+                        this.counter_ = new AdapterUtils.Counter_IList((IList)abscissaData);
+                        if (abscissaData is Double[])
+                            this.xDataGetter_ = new AdapterUtils.DataGetter_DoublesArray((Double[])abscissaData);
+                        else
+                            this.xDataGetter_ = new AdapterUtils.DataGetter_IList((IList)abscissaData);
+
+                        this.yDataGetter_ = new AdapterUtils.DataGetter_Count();
+                        return;
+					}
+					
+					else
+					{
+						// unknown.
+					}
+
+				}
+				else
+				{
+					// unknown
+				}
+
+			}
+
+			else if (dataSource is IList && dataMember == null)
+			{
+				if (dataSource is DataView)
+				{
+					DataView data = (DataView)dataSource;
+
+                    this.counter_ = new AdapterUtils.Counter_DataView(data);
+                    this.xDataGetter_ = new AdapterUtils.DataGetter_DataView(data, (string)abscissaData);
+                    this.yDataGetter_ = new AdapterUtils.DataGetter_DataView(data, (string)ordinateData);
+                    this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_DataView(data, (string)abscissaData);
+                    this.YAxisSuggester_ = new AdapterUtils.AxisSuggester_DataView(data, (string)ordinateData);
+                    return;
+				}
+
+				else
+				{
+                    this.YAxisSuggester_ = new AdapterUtils.AxisSuggester_IList((IList)dataSource);
+                    this.counter_ = new AdapterUtils.Counter_IList((IList)dataSource);
+                    this.yDataGetter_ = new AdapterUtils.DataGetter_IList((IList)dataSource);
+
+                    if ((ordinateData == null) && (abscissaData == null))
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_Auto((IList)dataSource);
+                        this.xDataGetter_ = new AdapterUtils.DataGetter_Count();
+                        return;
+					}
+
+					else if ((ordinateData == null) && (abscissaData is StartStep))
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_StartStep((StartStep)abscissaData, (IList)ordinateData);
+                        this.xDataGetter_ = new AdapterUtils.DataGetter_StartStep((StartStep)abscissaData);
+                        return;
+					}
+
+					else if ((ordinateData == null) && (abscissaData is IList))
+					{
+                        this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_IList((IList)abscissaData);
+                        this.xDataGetter_ = new AdapterUtils.DataGetter_IList((IList)abscissaData);
+                        return;
+					}
+
+					else
+					{
+						// unknown.
+					}
+				}
+			}
+
+			else if ( ((dataSource is DataTable) && (dataMember == null)) || (dataSource is DataSet) )
+			{
+				DataRowCollection rows = null;
+
+				if (dataSource is DataSet)
+				{
+					if (dataMember != null)
+					{
+						rows = ((DataTable)((DataSet)dataSource).Tables[dataMember]).Rows;
+					}
+					else
+					{
+						rows = ((DataTable)((DataSet)dataSource).Tables[0]).Rows;
+					}
+				}
+				else
+				{
+					rows = ((DataTable)dataSource).Rows;
+				}
+
+                this.yDataGetter_ = new AdapterUtils.DataGetter_Rows(rows, (string)ordinateData);
+                this.YAxisSuggester_ = new AdapterUtils.AxisSuggester_Rows(rows, (string)ordinateData);
+                this.counter_ = new AdapterUtils.Counter_Rows(rows);
+
+                if ((abscissaData is string) && (ordinateData is string))
+				{
+                    this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_Rows(rows, (string)abscissaData);
+                    this.xDataGetter_ = new AdapterUtils.DataGetter_Rows(rows, (string)abscissaData);
+                    return;
+				}
+				else if ((abscissaData == null) && (ordinateData is string))
+				{
+                    this.XAxisSuggester_ = new AdapterUtils.AxisSuggester_RowAuto(rows);
+                    this.xDataGetter_ = new AdapterUtils.DataGetter_Count();
+                    return;
+				}
+				else
+				{
+					// unknown.
+				}
+			}
+
+			else
+			{
+				// unknown.
+			}
+
+			throw new NPlotException( "Do not know how to interpret data provided to chart." );
+
+		}
+
+
+		/// <summary>
+		/// Returns the number of points.
+		/// </summary>
+		public int Count
+		{
+			get
+			{
+				return counter_.Count;
+			}
+		}
+
+
+		/// <summary>
+		/// Returns the ith point.
+		/// </summary>
+		public PointD this[int i] 
+		{
+			get
+			{
+				return new PointD( this.xDataGetter_.Get(i), this.yDataGetter_.Get(i) );
+			}
+		}
+
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing the data.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			return this.XAxisSuggester_.Get();
+		}
+
+
+		/// <summary>
+		/// Returns a y-axis that is suitable for drawing the data.
+		/// </summary>
+		/// <returns>A suitable y-axis.</returns>
+		public Axis SuggestYAxis()
+		{
+			Axis a = this.YAxisSuggester_.Get();
+			// TODO make 0.08 a parameter.
+			a.IncreaseRange( 0.08 );
+			return a;
+		}
+
+
+		/// <summary>
+		/// Writes data out as text. 
+		/// </summary>
+		/// <param name="sb">StringBuilder to write to.</param>
+		/// <param name="region">Only write out data in this region if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, only data in region is written, else all data is written.</param>
+		public void WriteData( System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion )
+		{
+			for (int i=0; i<this.Count;	++i)
+			{
+				if ( !(onlyInRegion && 
+					   (this[i].X >= region.X && this[i].X <= region.X+region.Width) &&
+					   (this[i].Y >= region.Y && this[i].Y <= region.Y+region.Height)) )
+					continue;
+
+				sb.Append( this[i].ToString() );
+				sb.Append( "\r\n" );
+			}
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/SequenceAdapter3D.cs b/nplot/nplot/SequenceAdapter3D.cs
new file mode 100644
index 0000000..27eaa55
--- /dev/null
+++ b/nplot/nplot/SequenceAdapter3D.cs
@@ -0,0 +1,28 @@
+// ******** experimental ******** 
+/*
+using System;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// This class will be analogous to SequenceAdapter for 2D charting.
+	/// Don't use it initially - offer only one method of getting in data
+	/// for similicity.
+	/// TODO. Not implemented.
+	/// </summary>
+	public class SequenceAdapter3D
+	{
+
+		/// <summary>
+		/// Constructor. TODO.
+		/// </summary>
+		public SequenceAdapter3D()
+		{
+		}
+
+	}
+
+}
+
+*/
\ No newline at end of file
diff --git a/nplot/nplot/StartStep.cs b/nplot/nplot/StartStep.cs
new file mode 100644
index 0000000..0cc7f38
--- /dev/null
+++ b/nplot/nplot/StartStep.cs
@@ -0,0 +1,111 @@
+/*
+NPlot - A charting library for .NET
+
+StartStep.cs
+Copyright (C) 2004
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Encapsulates a Start and Step value. This is useful for specifying a regularly spaced set of 
+	/// abscissa values.
+	/// </summary>
+	public class StartStep
+	{
+
+		private double start_;
+		private double step_;
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="start">the first value of the set of points specified by this object.</param>
+		/// <param name="step">the step that specifies the separation between successive points.</param>
+		public StartStep( double start, double step )
+		{
+			this.Start = start;
+			this.Step = step;
+		}
+
+
+		/// <summary>
+		/// The first value of the set of points specified by this object.
+		/// </summary>
+		public double Start
+		{
+			get
+			{
+				return start_;
+			}
+			set
+			{
+				this.start_ = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The step that specifies the separation between successive points.
+		/// </summary>
+		public double Step
+		{
+			get
+			{
+				return this.step_;
+			}
+			set
+			{
+				this.step_ = value;
+			}
+		}
+
+	}
+}
diff --git a/nplot/nplot/StepGradient.cs b/nplot/nplot/StepGradient.cs
new file mode 100644
index 0000000..e54c8fb
--- /dev/null
+++ b/nplot/nplot/StepGradient.cs
@@ -0,0 +1,149 @@
+/*
+NPlot - A charting library for .NET
+
+StepGradient.cs
+Copyright (C) 2004
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// Class for creating a rainbow legend.
+	/// </summary>
+	public class StepGradient : IGradient
+	{
+
+		/// <summary>
+		/// Types of step gradient defined.
+		/// </summary>
+		public enum Type
+		{
+			/// <summary>
+			/// Rainbow gradient type (colors of the rainbow)
+			/// </summary>
+			Rainbow,
+
+			/// <summary>
+			/// RGB gradient type (red, green blud).
+			/// </summary>
+			RGB
+		}
+
+		/// <summary>
+		/// Sets the type of step gradient.
+		/// </summary>
+		public Type StepType
+		{
+			get
+			{
+				return stepType_;
+			}
+			set
+			{
+				stepType_ = value;
+			}
+		}
+		Type stepType_ = Type.RGB;
+
+
+		/// <summary>
+		/// Default Constructor
+		/// </summary>
+		public StepGradient()
+		{
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="stepType">type of gradient</param>
+		public StepGradient( Type stepType )
+		{
+			stepType_  = stepType;
+		}
+
+		/// <summary>
+		/// Gets a color corresponding to a number between 0.0 and 1.0 inclusive. The color will
+		/// be a linear interpolation of the min and max colors.
+		/// </summary>
+		/// <param name="prop">the number to get corresponding color for (between 0.0 and 1.0)</param>
+		/// <returns>The color corresponding to the supplied number.</returns>
+		public Color GetColor( double prop )
+		{
+			switch (stepType_)
+			{
+				case Type.RGB:
+				{
+					if (prop < 1.0/3.0) return Color.Red;
+					if (prop < 2.0/3.0) return Color.Green;
+					return Color.Blue;
+				}
+				case Type.Rainbow:
+				{
+					if (prop < 0.125) return Color.Red;
+					if (prop < 0.25) return Color.Orange;
+					if (prop < 0.375) return Color.Yellow;
+					if (prop < 0.5) return Color.Green;
+					if (prop < 0.625) return Color.Cyan;
+					if (prop < 0.75) return Color.Blue;
+					if (prop < 0.825) return Color.Purple;
+					return Color.Pink;
+				}
+				default:
+				{
+					return Color.Black;
+				}
+			}
+
+		}
+
+	} 
+}
diff --git a/nplot/nplot/StepPlot.cs b/nplot/nplot/StepPlot.cs
new file mode 100644
index 0000000..41b6e4b
--- /dev/null
+++ b/nplot/nplot/StepPlot.cs
@@ -0,0 +1,347 @@
+/*
+NPlot - A charting library for .NET
+
+StepPlot.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 plotting data as a stepped line.
+	/// </summary>
+	public class StepPlot : BaseSequencePlot, IPlot, ISequencePlot
+	{
+
+		/// <summary>
+		/// Constructor.
+		/// </summary>
+		public StepPlot()
+		{
+			this.Center = false;
+		}
+
+
+		/// <summary>
+		/// Draws the step plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public virtual void Draw( Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			double leftCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMin, false);
+			double rightCutoff = xAxis.PhysicalToWorld(xAxis.PhysicalMax, false);
+
+			for (int i=0; i<data.Count; ++i)
+			{
+				PointD p1 = data[i];
+				if (Double.IsNaN(p1.X) || Double.IsNaN(p1.Y))
+				{
+					continue;
+				}
+
+				PointD p2;
+				PointD p3;
+				if (i+1 != data.Count)
+				{
+					p2 = data[i+1];
+					if (Double.IsNaN(p2.X) || Double.IsNaN(p2.Y))
+					{
+						continue;
+					}
+					p2.Y = p1.Y;
+					p3 = data[i+1];
+				}
+				else
+				{
+					p2 = data[i-1];
+					double offset = p1.X - p2.X;
+					p2.X = p1.X + offset;
+					p2.Y = p1.Y;
+					p3 = p2; 
+				}
+
+				if ( this.center_ )
+				{
+					double offset = ( p2.X - p1.X ) / 2.0f;
+					p1.X -= offset;
+					p2.X -= offset;
+					p3.X -= offset;
+				}
+
+				PointF xPos1 = xAxis.WorldToPhysical( p1.X, false );
+				PointF yPos1 = yAxis.WorldToPhysical( p1.Y, false );
+				PointF xPos2 = xAxis.WorldToPhysical( p2.X, false );
+				PointF yPos2 = yAxis.WorldToPhysical( p2.Y, false );
+				PointF xPos3 = xAxis.WorldToPhysical( p3.X, false );
+				PointF yPos3 = yAxis.WorldToPhysical( p3.Y, false );
+
+				// do horizontal clipping here, to speed up
+				if ((p1.X < leftCutoff || p1.X > rightCutoff ) &&
+					(p2.X < leftCutoff || p2.X > rightCutoff ) && 
+					(p3.X < leftCutoff || p3.X > rightCutoff ) )
+				{
+					continue;
+				}
+
+				if (!this.hideHorizontalSegments_)
+				{
+					if (scale_ != 1.0f)
+					{
+						float middle = (xPos2.X + xPos1.X) / 2.0f;
+						float width = xPos2.X - xPos1.X;
+						width *= this.scale_;
+						g.DrawLine( Pen, (int)(middle-width/2.0f), yPos1.Y, (int)(middle+width/2.0f), yPos2.Y );
+					}
+					else
+					{
+						g.DrawLine( Pen, xPos1.X, yPos1.Y, xPos2.X, yPos2.Y );
+					}
+				}
+				
+				if (!this.hideVerticalSegments_)
+				{
+					g.DrawLine( Pen, xPos2.X, yPos2.Y, xPos3.X, yPos3.Y );
+				}
+
+			}
+
+		}
+
+
+		/// <summary>
+		/// Returns an X-axis suitable for use by this plot. The axis will be one that is just long
+		/// enough to show all data.
+		/// </summary>
+		/// <returns>X-axis suitable for use by this plot.</returns>
+		public Axis SuggestXAxis()
+		{
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			if (data.Count < 2)
+			{
+				return data.SuggestXAxis();
+			}
+
+			// else
+
+			Axis a = data.SuggestXAxis();
+
+			PointD p1 = data[0];
+			PointD p2 = data[1];
+			PointD p3 = data[data.Count-2];
+			PointD p4 = data[data.Count-1];
+
+			double offset1;
+			double offset2;
+
+			if (!center_)
+			{
+				offset1 = 0.0f;
+				offset2 = p4.X - p3.X;
+			}
+			else
+			{
+				offset1 = (p2.X - p1.X)/2.0f;
+				offset2 = (p4.X - p3.X)/2.0f;
+			}
+
+			a.WorldMin -= offset1;
+			a.WorldMax += offset2;
+
+			return a;
+		}
+
+
+		/// <summary>
+		/// Returns an Y-axis suitable for use by this plot. The axis will be one that is just long
+		/// enough to show all data.
+		/// </summary>
+		/// <returns>Y-axis suitable for use by this plot.</returns>
+		public Axis SuggestYAxis()
+		{
+			SequenceAdapter data = 
+				new SequenceAdapter( this.DataSource, this.DataMember, this.OrdinateData, this.AbscissaData );
+
+			return data.SuggestYAxis();
+		}
+
+
+		/// <summary>
+		/// Gets or sets whether or not steps should be centered. If true, steps will be centered on the
+		/// X abscissa values. If false, the step corresponding to a given x-value will be drawn between 
+		/// this x-value and the next x-value at the current y-height.
+		/// </summary>
+		public bool Center
+		{
+			set
+			{
+				center_ = value;
+			}
+			get
+			{
+				return center_;
+			}
+		}
+		private bool center_;
+
+
+        /// <summary>
+        /// Draws a representation of this plot in the legend.
+        /// </summary>
+        /// <param name="g">The graphics surface on which to draw.</param>
+        /// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+        public virtual void DrawInLegend(Graphics g, Rectangle startEnd)
+        {
+            g.DrawLine(pen_, startEnd.Left, (startEnd.Top + startEnd.Bottom) / 2,
+                startEnd.Right, (startEnd.Top + startEnd.Bottom) / 2);
+        }
+
+
+        /// <summary>
+        /// The pen used to draw the plot
+        /// </summary>
+        public System.Drawing.Pen Pen
+        {
+            get
+            {
+                return pen_;
+            }
+            set
+            {
+                pen_ = value;
+            }
+        }
+        private System.Drawing.Pen pen_ = new Pen(Color.Black);
+
+
+        /// <summary>
+        /// The color of the pen used to draw lines in this plot.
+        /// </summary>
+        public System.Drawing.Color Color
+        {
+            set
+            {
+                if (pen_ != null)
+                {
+                    pen_.Color = value;
+                }
+                else
+                {
+                    pen_ = new Pen(value);
+                }
+            }
+            get
+            {
+                return pen_.Color;
+            }
+        }
+
+
+		/// <summary>
+		/// If true, then vertical lines are hidden.
+		/// </summary>
+		public bool HideVerticalSegments
+		{
+			get
+			{
+				return hideVerticalSegments_;
+			}
+			set
+			{
+				hideVerticalSegments_ = value;
+			}
+		}
+		bool hideVerticalSegments_ = false;
+
+
+		/// <summary>
+		/// If true, then vertical lines are hidden.
+		/// </summary>
+		public bool HideHorizontalSegments
+		{
+			get
+			{
+				return hideHorizontalSegments_;
+			}
+			set
+			{
+				hideHorizontalSegments_ = value;
+			}
+		}
+		bool hideHorizontalSegments_ = false;
+
+
+		/// <summary>
+		/// The horizontal line length is multiplied by this amount. Default
+		/// corresponds to a value of 1.0.
+		/// </summary>
+		public float WidthScale
+		{
+			get
+			{
+				return scale_;
+			}
+			set
+			{
+				scale_ = value;
+			}
+		}
+		private float scale_ = 1.0f;
+
+    }
+}
diff --git a/nplot/nplot/StrongName.snk b/nplot/nplot/StrongName.snk
new file mode 100644
index 0000000..db0f45f
Binary files /dev/null and b/nplot/nplot/StrongName.snk differ
diff --git a/nplot/nplot/TextItem.cs b/nplot/nplot/TextItem.cs
new file mode 100644
index 0000000..e4bbe4a
--- /dev/null
+++ b/nplot/nplot/TextItem.cs
@@ -0,0 +1,178 @@
+/*
+NPlot - A charting library for .NET
+
+TextItem.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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>
+	/// This class implements drawing text against two physical axes.
+	/// </summary>
+	public class TextItem : IDrawable
+	{
+		private void Init()
+		{
+			FontFamily fontFamily = new FontFamily("Arial");
+			font_ = new Font(fontFamily, 10, FontStyle.Regular, GraphicsUnit.Pixel);
+		}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="position">The position the text starts.</param>
+		/// <param name="text">The text.</param>
+		public TextItem( PointD position, string text )
+		{
+			start_ = position;
+			text_ = text;
+			Init();
+		}
+
+
+		/// <summary>
+		/// Text associated.
+		/// </summary>
+		public string Text 
+		{
+			get
+			{
+				return text_;
+			}
+			set
+			{
+				text_ = value;
+			}
+		}
+		private string text_ = "";
+
+
+		/// <summary>
+		/// The starting point for the text.
+		/// </summary>
+		public PointD Start
+		{
+			get
+			{
+				return start_;
+			}
+			set
+			{
+				start_ = value;
+			}
+		}
+		private PointD start_;
+
+
+		/// <summary>
+		/// Draws the text on a plot surface.
+		/// </summary>
+		/// <param name="g">graphics surface on which to draw</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw( System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis )
+		{
+			Point startPoint = new Point( 
+				(int)xAxis.WorldToPhysical( start_.X, true ).X,
+				(int)yAxis.WorldToPhysical( start_.Y, true ).Y );
+
+			g.DrawString(text_, font_, textBrush_,(int)startPoint.X,(int)startPoint.Y);
+		}
+
+
+		/// <summary>
+		/// The brush used to draw the text.
+		/// </summary>
+		public Brush TextBrush
+		{
+			get
+			{
+				return textBrush_;
+			}
+			set
+			{
+				textBrush_ = value;
+			}
+		}
+
+	
+		/// <summary>
+		/// Set the text to be drawn with a solid brush of this color.
+		/// </summary>
+		public Color TextColor
+		{
+			set
+			{
+				textBrush_ = new SolidBrush( value );
+			}
+		}
+	
+		/// <summary>
+		/// The font used to draw the text associated with the arrow.
+		/// </summary>
+		public Font TextFont
+		{
+			get
+			{
+				return this.font_;
+			}
+			set
+			{
+				this.font_ = value;
+			}
+		}
+
+		private Brush textBrush_ = new SolidBrush( Color.Black );
+		private Pen pen_ = new Pen( Color.Black );
+		private Font font_;
+	}
+}
diff --git a/nplot/nplot/TradingDateTimeAxis.cs b/nplot/nplot/TradingDateTimeAxis.cs
new file mode 100644
index 0000000..336a979
--- /dev/null
+++ b/nplot/nplot/TradingDateTimeAxis.cs
@@ -0,0 +1,713 @@
+/*
+NPlot - A charting library for .NET
+
+TradingDateTimeAxis.cs
+Copyright (C) 2006
+Pawel Konieczny
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+source file the above copyright notice, this list of conditions
+and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts
+there-of must either:
+
+(a) allow any user of the product to obtain a complete machine-
+readable copy of the corresponding source code for the
+product and the version of NPlot used for a charge no more
+than your cost of physically performing source distribution,
+on a medium customarily used for software interchange, or:
+
+(b) reproduce the following text in the documentation, about
+box or other materials intended to be read by human users
+of the product that is provided to every human user of the
+product:
+
+"This product includes software developed as
+part of the NPlot library project available
+from: http://www.nplot.com/";
+
+The words "This product" may optionally be replace with
+the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Collections;
+
+
+namespace NPlot
+{
+
+	/// <summary>
+	/// Provides a DateTime axis that removes non-trading days.
+	/// </summary>
+	public class TradingDateTimeAxis : DateTimeAxis
+	{
+		// we keep shadow "virtual" copies of WorldMin/Max for speed
+		// which are already remapped, so it is essential that changes
+		// to WorldMin/Max are captured here
+
+
+		/// <summary>
+		/// The axis world min value.
+		/// </summary>
+		public override double WorldMin
+		{
+			get 
+			{ 
+				return base.WorldMin; 
+			}
+			set
+			{
+				base.WorldMin = value;
+				virtualWorldMin_ = SparseWorldRemap(value);
+			}
+		}
+		private double virtualWorldMin_ = double.NaN;
+
+
+		/// <summary>
+		/// The axis world max value.
+		/// </summary>
+		public override double WorldMax
+		{
+			get 
+			{ 
+				return base.WorldMax;
+			}
+			set
+			{
+				base.WorldMax = value;
+				virtualWorldMax_ = SparseWorldRemap(value);
+			}
+		}
+		private double virtualWorldMax_ = double.NaN;
+
+
+		/// <summary>
+		/// Optional time at which trading begins.
+		/// All data points earlied than that (same day) will be collapsed.
+		/// </summary>
+		public virtual TimeSpan StartTradingTime
+		{
+			get 
+			{ 
+				return new TimeSpan(startTradingTime_);
+			}
+			set 
+			{
+				startTradingTime_ = value.Ticks;
+				tradingTimeSpan_ = endTradingTime_ - startTradingTime_;
+			}
+		}
+		private long startTradingTime_;
+
+		/// <summary>
+		/// Optional time at which trading ends.
+		/// All data points later than that (same day) will be collapsed.
+		/// </summary>
+		public virtual TimeSpan EndTradingTime
+		{
+			get 
+			{
+				return new TimeSpan(endTradingTime_); 
+			}
+			set 
+			{
+				endTradingTime_ = value.Ticks;
+				tradingTimeSpan_ = endTradingTime_ - startTradingTime_;
+			}
+		}
+		private long endTradingTime_;
+
+		private long tradingTimeSpan_;
+
+
+		/// <summary>
+		/// Get whether or not this axis is linear.
+		/// </summary>
+		public override bool IsLinear
+		{
+			get
+			{
+				return false;
+			}
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public TradingDateTimeAxis() : base()
+		{
+			Init();
+		}
+
+		/// <summary>
+		/// Copy Constructor
+		/// </summary>
+		/// <param name="a">construct a TradingDateTimeAxis based on this provided axis.</param>
+		public TradingDateTimeAxis(Axis a) : base(a)
+		{
+			Init();
+			if (a is TradingDateTimeAxis)
+				DoClone((TradingDateTimeAxis)a, this);
+			else if (a is DateTimeAxis)
+				DoClone((DateTimeAxis)a, this);
+			else
+			{
+				DoClone(a, this);
+				this.NumberFormat = null;
+			}
+		}
+
+
+		/// <summary>
+		/// Helper function for constructors.
+		/// </summary>
+		private void Init()
+		{
+			startTradingTime_ = 0;
+			endTradingTime_ = TimeSpan.TicksPerDay;
+			tradingTimeSpan_ = endTradingTime_ - startTradingTime_;
+			virtualWorldMin_ = SparseWorldRemap(WorldMin);
+			virtualWorldMax_ = SparseWorldRemap(WorldMax);
+		}
+
+
+		/// <summary>
+		/// Deep copy of DateTimeAxis.
+		/// </summary>
+		/// <returns>A copy of the DateTimeAxis Class.</returns>
+		public override object Clone()
+		{
+			TradingDateTimeAxis a = new TradingDateTimeAxis();
+			// ensure that this isn't being called on a derived type. If it is, then oh no!
+			if (this.GetType() != a.GetType())
+			{
+				throw new NPlotException( "Clone not defined in derived type. Help!" );
+			}
+			DoClone( this, a );
+			return a;
+		}
+
+
+		/// <summary>
+		/// Helper method for Clone.
+		/// </summary>
+		/// <param name="a">The cloned target object.</param>
+		/// <param name="b">The cloned source object.</param>
+		protected static void DoClone(TradingDateTimeAxis b, TradingDateTimeAxis a)
+		{
+			DateTimeAxis.DoClone(b, a);
+			a.startTradingTime_ = b.startTradingTime_;
+			a.endTradingTime_ = b.endTradingTime_;
+			a.tradingTimeSpan_ = b.tradingTimeSpan_;
+			a.WorldMin = b.WorldMin;
+			a.WorldMax = b.WorldMax;
+		}
+
+
+		/// <summary>
+		/// World to physical coordinate transform.
+		/// </summary>
+		/// <param name="coord">The coordinate value to transform.</param>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="clip">if false, then physical value may extend outside worldMin / worldMax. If true, the physical value returned will be clipped to physicalMin or physicalMax if it lies outside this range.</param>
+		/// <returns>The transformed coordinates.</returns>
+		/// <remarks>Not sure how much time is spent in this often called function. If it's lots, then
+		/// worth optimizing (there is scope to do so).</remarks>
+		public override PointF WorldToPhysical(
+			double coord,
+			PointF physicalMin,
+			PointF physicalMax,
+			bool clip)
+		{
+
+			// (1) account for reversed axis. Could be tricky and move
+			// this out, but would be a little messy.
+
+			PointF _physicalMin;
+			PointF _physicalMax;
+
+			if (this.Reversed)
+			{
+				_physicalMin = physicalMax;
+				_physicalMax = physicalMin;
+			}
+			else
+			{
+				_physicalMin = physicalMin;
+				_physicalMax = physicalMax;
+			}
+
+
+			// (2) if want clipped value, return extrema if outside range.
+
+			if (clip)
+			{
+				if (WorldMin < WorldMax)
+				{
+					if (coord > WorldMax)
+					{
+						return _physicalMax;
+					}
+					if (coord < WorldMin)
+					{
+						return _physicalMin;
+					}
+				}
+				else
+				{
+					if (coord < WorldMax)
+					{
+						return _physicalMax;
+					}
+					if (coord > WorldMin)
+					{
+						return _physicalMin;
+					}
+				}
+			}
+
+
+			// (3) we are inside range or don't want to clip.
+
+			coord = SparseWorldRemap(coord);
+			double range = virtualWorldMax_ - virtualWorldMin_;
+			double prop = (double)((coord - virtualWorldMin_) / range);
+			//double range = WorldMax - WorldMin;
+			//double prop = (double)((coord - WorldMin) / range);
+			//if (range1 != range)
+			//    range1 = range;
+
+			// Force clipping at bounding box largeClip times that of real bounding box
+			// anyway. This is effectively at infinity.
+			const double largeClip = 100.0;
+			if (prop > largeClip && clip)
+				prop = largeClip;
+
+			if (prop < -largeClip && clip)
+				prop = -largeClip;
+
+			if (range == 0)
+			{
+				if (coord >= virtualWorldMin_)
+					prop = largeClip;
+
+				if (coord < virtualWorldMin_)
+					prop = -largeClip;
+			}
+
+			// calculate the physical coordinate.
+			PointF offset = new PointF(
+				(float)(prop * (_physicalMax.X - _physicalMin.X)),
+				(float)(prop * (_physicalMax.Y - _physicalMin.Y)));
+
+			return new PointF(_physicalMin.X + offset.X, _physicalMin.Y + offset.Y);
+		}
+
+
+		/// <summary>
+		/// Transforms a physical coordinate to an axis world 
+		/// coordinate given the physical extremites of the axis.
+		/// </summary>
+		/// <param name="p">the point to convert</param>
+		/// <param name="physicalMin">the physical minimum extremity of the axis</param>
+		/// <param name="physicalMax">the physical maximum extremity of the axis</param>
+		/// <param name="clip">whether or not to clip the world value to lie in the range of the axis if it is outside.</param>
+		/// <returns></returns>
+		public override double PhysicalToWorld(
+			PointF p,
+			PointF physicalMin,
+			PointF physicalMax,
+			bool clip)
+		{
+			// (1) account for reversed axis. Could be tricky and move
+			// this out, but would be a little messy.
+
+			PointF _physicalMin;
+			PointF _physicalMax;
+
+			if (this.Reversed)
+			{
+				_physicalMin = physicalMax;
+				_physicalMax = physicalMin;
+			}
+			else
+			{
+				_physicalMin = physicalMin;
+				_physicalMax = physicalMax;
+			}
+
+			// normalised axis dir vector
+			float axis_X = _physicalMax.X - _physicalMin.X;
+			float axis_Y = _physicalMax.Y - _physicalMin.Y;
+			float len = (float)Math.Sqrt(axis_X * axis_X + axis_Y * axis_Y);
+			axis_X /= len;
+			axis_Y /= len;
+
+			// point relative to axis physical minimum.
+			PointF posRel = new PointF(p.X - _physicalMin.X, p.Y - _physicalMin.Y);
+
+			// dist of point projection on axis, normalised.
+			float prop = (axis_X * posRel.X + axis_Y * posRel.Y) / len;
+
+			//double world = prop * (WorldMax - WorldMin) + WorldMin;
+			double world = prop * (virtualWorldMax_ - virtualWorldMin_) + virtualWorldMin_;
+			world = ReverseSparseWorldRemap(world);
+
+			// if want clipped value, return extrema if outside range.
+			if (clip)
+			{
+				world = Math.Max(world, WorldMin);
+				world = Math.Min(world, WorldMax);
+			}
+
+			return world;
+		}
+
+
+		/// <summary>
+		/// Remap a world coordinate into a "virtual" world, where non-trading dates and times are collapsed.
+		/// </summary>
+		/// <remarks>
+		/// This code works under asumption that there are exactly 24*60*60 seconds in a day
+		/// This is strictly speaking not correct but apparently .NET 2.0 does not count leap seconds.
+		/// Luckilly, Ticks == 0  =~= 0001-01-01T00:00 =~= Monday
+		/// First tried a version fully on floating point arithmetic,
+		/// but failed hopelessly due to rounding errors.
+		/// </remarks>
+		/// <param name="coord">world coordinate to transform.</param>
+		/// <returns>equivalent virtual world coordinate.</returns>
+		protected double SparseWorldRemap(double coord)
+		{
+			long ticks = (long)coord;
+			long whole_days = ticks / TimeSpan.TicksPerDay;
+			long ticks_in_last_day = ticks % TimeSpan.TicksPerDay;
+			long full_weeks = whole_days / 7;
+			long days_in_last_week = whole_days % 7;
+			if (days_in_last_week >= 5)
+			{
+				days_in_last_week = 5;
+				ticks_in_last_day = 0;
+			}
+			if (ticks_in_last_day < startTradingTime_) ticks_in_last_day = startTradingTime_;
+			else if (ticks_in_last_day > endTradingTime_) ticks_in_last_day = endTradingTime_;
+			ticks_in_last_day -= startTradingTime_;
+
+			long whole_working_days = (full_weeks * 5 + days_in_last_week);
+			long working_ticks = whole_working_days * tradingTimeSpan_;
+			long new_ticks = working_ticks + ticks_in_last_day;
+			return (double)new_ticks;
+		}
+
+
+		/// <summary>
+		/// Remaps a "virtual" world coordinates back to true world coordinates.
+		/// </summary>
+		/// <param name="coord">virtual world coordinate to transform.</param>
+		/// <returns>equivalent world coordinate.</returns>
+		protected double ReverseSparseWorldRemap(double coord)
+		{
+			long ticks = (long)coord;
+			//ticks += startTradingTime_;
+			long ticks_in_last_day = ticks % tradingTimeSpan_;
+			ticks /= tradingTimeSpan_;
+			long full_weeks = ticks / 5;
+			long week_part = ticks % 5;
+
+			long day_ticks = (full_weeks * 7 + week_part) * TimeSpan.TicksPerDay;
+			return (double)(day_ticks + ticks_in_last_day + startTradingTime_);
+		}
+
+
+		/// <summary>
+		/// Adds a delta amount to the given world coordinate in such a way that
+		/// all "sparse gaps" are skipped.  In other words, the returned value is
+		/// in delta distance from the given in the "virtual" world.
+		/// </summary>
+		/// <param name="coord">world coordinate to shift.</param>
+		/// <param name="delta">shif amount in "virtual" units.</param>
+		/// <returns></returns>
+		public double SparseWorldAdd(double coord, double delta)
+		{
+			return ReverseSparseWorldRemap(SparseWorldRemap(coord) + delta);
+		}
+
+
+		/// <summary>
+		/// World extent in virtual (sparse) units.
+		/// </summary>
+		public double SparseWorldLength
+		{
+			get
+			{
+				return SparseWorldRemap(WorldMax) - SparseWorldRemap(WorldMin);
+			}
+		}
+
+
+		/// <summary>
+		/// Check whether the given coordinate falls within defined trading hours.
+		/// </summary>
+		/// <param name="coord">world coordinate in ticks to check.</param>
+		/// <returns>true if in trading hours, false if in non-trading gap.</returns>
+		public bool WithinTradingHours(double coord)
+		{
+			long ticks = (long)coord;
+			long whole_days = ticks / TimeSpan.TicksPerDay;
+			long ticks_in_last_day = ticks % TimeSpan.TicksPerDay;
+			long days_in_last_week = whole_days % 7;
+			if (days_in_last_week >= 5)
+				return false;
+
+			if (ticks_in_last_day < startTradingTime_) return false;
+			if (ticks_in_last_day >= endTradingTime_) return false;
+
+			return true;
+		}
+
+
+		/// <summary>
+		/// Check whether the given coordinate falls on trading days.
+		/// </summary>
+		/// <param name="coord">world coordinate in ticks to check.</param>
+		/// <returns>true if on Mon - Fri.</returns>
+		public bool OnTradingDays(double coord)
+		{
+			long ticks = (long)coord;
+			long whole_days = ticks / TimeSpan.TicksPerDay;
+			long days_in_last_week = whole_days % 7;
+			return (days_in_last_week < 5);
+		}
+
+
+		/// <summary>
+		/// Determines the positions of all Large and Small ticks.
+		/// </summary>
+		/// <remarks>
+		/// The method WorldTickPositions_FirstPass() from the base works just fine, except that it
+		/// does not account for non-trading gaps in time, therefore, when less than two days are visible
+		/// an own algorithm is used (to show intraday time).  Otherwise the base class implementation is used
+		/// but the output is corrected to remove ticks on non-trading days (Sat, Sun).
+		/// </remarks>
+		/// <param name="physicalMin">The physical position corresponding to the world minimum of the axis.</param>
+		/// <param name="physicalMax">The physical position corresponding to the world maximum of the axis.</param>
+		/// <param name="largeTickPositions">ArrayList containing the positions of the large ticks.</param>
+		/// <param name="smallTickPositions">null</param>
+		internal override void WorldTickPositions_FirstPass(
+			Point physicalMin,
+			Point physicalMax,
+			out ArrayList largeTickPositions,
+			out ArrayList smallTickPositions
+			)
+		{
+			if (LargeTickStep != TimeSpan.Zero || SparseWorldLength > 2.0 * (double)tradingTimeSpan_) // utilise base class
+			{
+				ArrayList largeTickPositions_FirstPass;
+				base.WorldTickPositions_FirstPass(physicalMin, physicalMax, out largeTickPositions_FirstPass, out smallTickPositions);
+
+				if (largeTickPositions_FirstPass.Count < 2)
+				{
+					// leave it alone, whatever that single tick may be (better something than nothing...)
+					largeTickPositions = largeTickPositions_FirstPass;
+				}
+				else if ((double)largeTickPositions_FirstPass[1] - (double)largeTickPositions_FirstPass[0] > 27.0 * (double)TimeSpan.TicksPerDay)
+				{
+					// For distances between ticks in months or longer, just accept all ticks
+					largeTickPositions = largeTickPositions_FirstPass;
+				}
+				else
+				{
+					// for daily ticks, ignore non-trading hours but obey (skip) non-trading days
+					largeTickPositions = new ArrayList();
+					foreach (object tick in largeTickPositions_FirstPass)
+					{
+						if (OnTradingDays((double)tick))
+							largeTickPositions.Add(tick);
+					}
+				}
+			}
+			else // intraday ticks, own algorithm
+			{
+				smallTickPositions = null;
+				largeTickPositions = new ArrayList();
+
+				TimeSpan timeLength = new TimeSpan((long)SparseWorldLength);
+				DateTime worldMinDate = new DateTime( (long)this.WorldMin );
+				DateTime worldMaxDate = new DateTime( (long)this.WorldMax );
+
+				DateTime currentTickDate;
+				long skip; // in time ticks
+
+				// The following if-else flow establishes currentTickDate to the beginning of series
+				// and skip to the optimal distance between ticks
+
+				// if less than 10 minutes, then large ticks on second spacings.
+
+				if ( timeLength < new TimeSpan(0,0,10,0,0) )
+				{
+					this.LargeTickLabelType_ = LargeTickLabelType.hourMinuteSeconds;
+
+					int secondsSkip;
+
+					if (timeLength < new TimeSpan( 0,0,0,10,0 ) )
+						secondsSkip = 1;
+					else if ( timeLength < new TimeSpan(0,0,0,20,0) )
+						secondsSkip = 2;
+					else if ( timeLength < new TimeSpan(0,0,0,50,0) )
+						secondsSkip = 5;
+					else if ( timeLength < new TimeSpan(0,0,2,30,0) )
+						secondsSkip = 15;
+					else
+						secondsSkip = 30;
+
+					int second = worldMinDate.Second;
+					second -= second % secondsSkip;
+
+					currentTickDate = new DateTime(
+						worldMinDate.Year,
+						worldMinDate.Month,
+						worldMinDate.Day,
+						worldMinDate.Hour,
+						worldMinDate.Minute,
+						second,0 );
+
+					skip = secondsSkip * TimeSpan.TicksPerSecond;
+				}
+
+					// Less than 2 hours, then large ticks on minute spacings.
+
+				else if ( timeLength < new TimeSpan(0,2,0,0,0) )
+				{
+					this.LargeTickLabelType_ = LargeTickLabelType.hourMinute;
+
+					int minuteSkip;
+
+					if ( timeLength < new TimeSpan(0,0,10,0,0) )
+						minuteSkip = 1;
+					else if ( timeLength < new TimeSpan(0,0,20,0,0) )
+						minuteSkip = 2;
+					else if ( timeLength < new TimeSpan(0,0,50,0,0) )
+						minuteSkip = 5;
+					else if ( timeLength < new TimeSpan(0,2,30,0,0) )
+						minuteSkip = 15;
+					else //( timeLength < new TimeSpan( 0,5,0,0,0) )
+						minuteSkip = 30;
+
+					int minute = worldMinDate.Minute;
+					minute -= minute % minuteSkip;
+
+					currentTickDate = new DateTime(
+						worldMinDate.Year,
+						worldMinDate.Month,
+						worldMinDate.Day,
+						worldMinDate.Hour,
+						minute,0,0 );
+
+					skip = minuteSkip * TimeSpan.TicksPerMinute;
+				}
+
+					// Else large ticks on hour spacings.
+
+				else
+				{
+					this.LargeTickLabelType_ = LargeTickLabelType.hourMinute;
+
+					int hourSkip;
+					if (timeLength < new TimeSpan(0, 10, 0, 0, 0))
+						hourSkip = 1;
+					else if (timeLength < new TimeSpan(0, 20, 0, 0, 0))
+						hourSkip = 2;
+					else
+						hourSkip = 6;
+
+
+					int hour = worldMinDate.Hour;
+					hour -= hour % hourSkip;
+
+					currentTickDate = new DateTime(
+						worldMinDate.Year,
+						worldMinDate.Month,
+						worldMinDate.Day,
+						hour, 0, 0, 0);
+
+					skip = hourSkip * TimeSpan.TicksPerHour;
+				}
+
+
+				// place ticks
+
+				while (currentTickDate < worldMaxDate)
+				{
+					double world = (double)currentTickDate.Ticks;
+
+					if (!WithinTradingHours(world))
+					{
+						// add gap boundary instead
+						world = ReverseSparseWorldRemap(SparseWorldRemap(world)); // moves forward
+						long gap = (long)world;
+						gap -= gap % skip;
+						currentTickDate = new DateTime(gap);
+					}
+
+					if (world >= this.WorldMin && world <= this.WorldMax)
+					{
+						largeTickPositions.Add(world);
+					}
+
+					currentTickDate = currentTickDate.AddTicks(skip);
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Get an appropriate label name, given the DateTime of a label
+		/// </summary>
+		/// <param name="tickDate">the DateTime to get the label name for</param>
+		/// <returns>A label name appropriate to the supplied DateTime.</returns>
+		protected override string LargeTickLabel(DateTime tickDate)
+		{
+			string label;
+
+			if ( this.NumberFormat == null
+				&& (LargeTickLabelType_ == LargeTickLabelType.hourMinute ||
+				LargeTickLabelType_ == LargeTickLabelType.hourMinuteSeconds)
+				&& tickDate.TimeOfDay == StartTradingTime)
+			{
+				// in such case always show the day date
+				label = (tickDate.Day).ToString();
+				label += " ";
+				label += tickDate.ToString("MMM");
+			}
+			else
+			{
+				label = base.LargeTickLabel(tickDate);
+			}
+			return label;
+		}
+	}
+}
+
diff --git a/nplot/nplot/Transform2D.cs b/nplot/nplot/Transform2D.cs
new file mode 100644
index 0000000..0248964
--- /dev/null
+++ b/nplot/nplot/Transform2D.cs
@@ -0,0 +1,190 @@
+/*
+NPlot - A charting library for .NET
+
+Transform2D.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 ) );
+			}
+
+		}
+
+
+	}
+}
diff --git a/nplot/nplot/Utils.cs b/nplot/nplot/Utils.cs
new file mode 100644
index 0000000..c2d2a1b
--- /dev/null
+++ b/nplot/nplot/Utils.cs
@@ -0,0 +1,379 @@
+/*
+NPlot - A charting library for .NET
+
+Utils.cs
+Copyright (C) 2003-2004
+Matt Howlett 
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Drawing2D;
+using System.Data;
+using System.Collections;
+
+namespace NPlot
+{
+	/// <summary>
+	/// General purpose utility functions used internally.
+	/// </summary>
+	internal class Utils
+	{
+
+		/// <summary>
+		/// Numbers less than this are considered insignificant. This number is
+		/// bigger than double.Epsilon.
+		/// </summary>
+		public const double Epsilon = double.Epsilon * 1000.0;
+		
+
+		/// <summary>
+		/// Returns true if the absolute difference between parameters is less than Epsilon
+		/// </summary>
+		/// <param name="a">first number to compare</param>
+		/// <param name="b">second number to compare</param>
+		/// <returns>true if equal, false otherwise</returns>
+		public static bool DoubleEqual( double a, double b )
+		{
+			if ( System.Math.Abs(a-b) < Epsilon )
+			{
+				return true;
+			}
+			return false;
+		}
+
+
+		/// <summary>
+		/// Swaps the value of two doubles.
+		/// </summary>
+		/// <param name="a">first value to swap.</param>
+		/// <param name="b">second value to swap.</param>
+		public static void Swap( ref double a, ref double b )
+		{
+			double c = a;
+			a = b;
+			b = c;
+		}
+
+
+		/// <summary>
+		/// Calculate the distance between two points, a and b.
+		/// </summary>
+		/// <param name="a">First point</param>
+		/// <param name="b">Second point</param>
+		/// <returns>Distance between points a and b</returns>
+		public static float Distance( PointF a, PointF b )
+		{ 
+			return (float)System.Math.Sqrt( (a.X - b.X)*(a.X - b.X) + (a.Y - b.Y)*(a.Y - b.Y) );
+		}
+
+
+		/// <summary>
+		/// Calculate the distance between two points, a and b.
+		/// </summary>
+		/// <param name="a">First point</param>
+		/// <param name="b">Second point</param>
+		/// <returns>Distance between points a and b</returns>
+		public static int Distance( Point a, Point b )
+		{
+			return (int)System.Math.Sqrt( (a.X - b.X)*(a.X - b.X) + (a.Y - b.Y)*(a.Y - b.Y) );
+		}
+
+
+		/// <summary>
+		/// Converts an object of type DateTime or IConvertible to double representation. 
+		/// Mapping is 1:1. Note: the System.Convert.ToDouble method can not convert a boxed 
+		/// DateTime to double. This implementation can - but the "is" check probably makes
+		/// it much slower.
+		/// </summary>
+		/// <remarks>Compare speed with System.Convert.ToDouble and revise code that calls this if significant speed difference.</remarks>
+		/// <param name="o">The object to convert to double.</param>
+		/// <returns>double value associated with the object.</returns>
+		public static double ToDouble( object o )
+		{
+			if (o is DateTime)
+			{
+				return (double)(((DateTime)o).Ticks);
+			}
+
+			else if (o is IConvertible)
+			{
+				return System.Convert.ToDouble(o);
+			}
+
+			throw new NPlotException( "Invalid datatype" );
+		}
+
+
+		/// <summary>
+		/// Returns the minimum and maximum values in an IList. The members of the list
+		/// can be of different types - any type for which the function Utils.ConvertToDouble
+		/// knows how to convert into a double.
+		/// </summary>
+		/// <param name="a">The IList to search.</param>
+		/// <param name="min">The minimum value.</param>
+		/// <param name="max">The maximum value.</param>
+		/// <returns>true if min max set, false otherwise (a == null or zero length).</returns>
+		public static bool ArrayMinMax( IList a, out double min, out double max )
+		{
+			if ( a == null || a.Count == 0 )
+			{
+				min = 0.0;
+				max = 0.0;
+				return false;
+			}
+
+			min = Utils.ToDouble(a[0]);
+			max = Utils.ToDouble(a[0]);
+			
+			foreach ( object o in a )
+			{
+
+				double e = Utils.ToDouble(o);
+
+				if ( (min.Equals (double.NaN)) && (!e.Equals (double.NaN)) )
+				{
+					// if min/max are double.NaN and the current value not, then
+					// set them to the current value.
+					min = e;
+					max = e;
+				}
+				if (!double.IsNaN(e))
+				{
+					if (e < min)
+					{
+						min = e;
+					}
+					if (e > max)
+					{
+						max = e;
+					}
+				}
+			}
+			
+			if (min.Equals (double.NaN))
+			{
+				// if min == double.NaN, then max is also double.NaN
+				min = 0.0;
+				max = 0.0;
+				return false;
+			}
+
+			return true;
+		}
+
+
+		/// <summary>
+		/// Returns the minimum and maximum values in a DataRowCollection.
+		/// </summary>
+		/// <param name="rows">The row collection to search.</param>
+		/// <param name="min">The minimum value.</param>
+		/// <param name="max">The maximum value.</param>
+		/// <param name="columnName">The name of the column in the row collection to search over.</param>
+		/// <returns>true is min max set, false otherwise (a = null or zero length).</returns>
+		public static bool RowArrayMinMax( DataRowCollection rows, 
+			out double min, out double max, string columnName )
+		{
+			// double[] is a reference type and can be null, if it is then I reckon the best
+			// values for min and max are also null. double is a value type so can't be set
+			//	to null. So min an max return object, and we understand that if it is not null
+			// it is a boxed double (same trick I use lots elsewhere in the lib). The 
+			// wonderful comment I didn't write at the top should explain everything.
+			if ( rows == null || rows.Count == 0 )
+			{
+				min = 0.0;
+				max = 0.0;
+				return false;
+			}
+
+			min = Utils.ToDouble( (rows[0])[columnName] );
+			max = Utils.ToDouble( (rows[0])[columnName] );
+
+			foreach ( DataRow r in rows ) 
+			{
+				double e = Utils.ToDouble( r[columnName] );
+
+				if ( (min.Equals (double.NaN)) && (!e.Equals (double.NaN)) )
+				{
+					// if min/max are double.NaN and the current value not, then
+					// set them to the current value.
+					min = e;
+					max = e;
+				}
+
+				if (!double.IsNaN(e))
+				{
+					if (e < min)
+					{
+						min = e;
+					}
+					if (e > max)
+					{
+						max = e;
+					}
+				}
+			}
+			if (min.Equals (double.NaN))
+			{
+				// if min == double.NaN, then max is also double.NaN
+				min = 0.0;
+				max = 0.0;
+				return false;
+			}
+
+			return true;
+
+		}
+
+
+		/// <summary>
+		/// Returns the minimum and maximum values in a DataView.
+		/// </summary>
+		/// <param name="data">The DataView to search.</param>
+		/// <param name="min">The minimum value.</param>
+		/// <param name="max">The maximum value.</param>
+		/// <param name="columnName">The name of the column in the row collection to search over.</param>
+		/// <returns>true is min max set, false otherwise (a = null or zero length).</returns>
+		public static bool DataViewArrayMinMax( DataView data, 
+			out double min, out double max, string columnName )
+		{
+			// double[] is a reference type and can be null, if it is then I reckon the best
+			// values for min and max are also null. double is a value type so can't be set
+			//	to null. So min an max return object, and we understand that if it is not null
+			// it is a boxed double (same trick I use lots elsewhere in the lib). The 
+			// wonderful comment I didn't write at the top should explain everything.
+			if ( data == null || data.Count == 0 )
+			{
+				min = 0.0;
+				max = 0.0;
+				return false;
+			}
+
+			min = Utils.ToDouble( (data[0])[columnName] );
+			max = Utils.ToDouble( (data[0])[columnName] );
+
+			for (int i=0; i<data.Count; ++i)
+			{
+
+				double e = Utils.ToDouble( data[i][columnName] );
+
+				if (e < min)
+				{
+					min = e;
+				}
+			
+				if (e > max) 
+				{
+					max = e;
+				}
+			}
+
+			return true;
+
+		}
+
+
+		/// <summary>
+		/// Returns unit vector along the line  a->b.
+		/// </summary>
+		/// <param name="a">line start point.</param>
+		/// <param name="b">line end point.</param>
+		/// <returns>The unit vector along the specified line.</returns>
+		public static PointF UnitVector( PointF a, PointF b )
+		{
+			PointF dir = new PointF( b.X - a.X, b.Y - a.Y );
+			double dirNorm = System.Math.Sqrt( dir.X*dir.X + dir.Y*dir.Y );
+			if ( dirNorm > 0.0f )
+			{
+				dir = new PointF( 
+					(float)((1.0f/dirNorm)*dir.X), 
+					(float)((1.0f/dirNorm)*dir.Y) ); // normalised axis direction vector
+			}
+			return dir;
+		}
+
+		/// <summary>
+		/// Get a Font exactly the same as the passed in one, except for scale factor.
+		/// </summary>
+		/// <param name="initial">The font to scale.</param>
+		/// <param name="scale">Scale by this factor.</param>
+		/// <returns>The scaled font.</returns>
+		public static Font ScaleFont( Font initial, double scale )
+		{
+			FontStyle fs = initial.Style;
+			GraphicsUnit gu = initial.Unit;
+			double sz = initial.Size;
+			sz = sz * scale ;
+			string nm = initial.Name;
+			return new Font( nm, (float)sz, fs, gu );
+		}
+
+
+		/// <summary>
+		/// Creates a bitmap from another that is tiled size times in each direction.
+		/// </summary>
+		/// <param name="image">bitmap to tile</param>
+		/// <param name="size">number of times to tile in each direction.</param>
+		/// <returns>the tiled bitmap.</returns>
+		public static System.Drawing.Bitmap TiledImage( System.Drawing.Bitmap image, Size size )
+		{
+			System.Drawing.Bitmap final = new System.Drawing.Bitmap( size.Width, size.Height );
+			
+			for (int i=0; i<(size.Width / image.Width)+1; ++i)
+			{
+				for (int j=0; j<(size.Height / image.Height)+1; ++j)
+				{
+					Graphics.FromImage( final ).DrawImage( image, i*image.Width, j*image.Height );
+				}
+			}
+
+			return final;
+		}
+
+	}
+
+
+}
diff --git a/nplot/nplot/VerticalLine.cs b/nplot/nplot/VerticalLine.cs
new file mode 100644
index 0000000..cc0e261
--- /dev/null
+++ b/nplot/nplot/VerticalLine.cs
@@ -0,0 +1,293 @@
+/*
+NPlot - A charting library for .NET
+
+VerticalLine.cs
+Copyright (C) 2003
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 drawing a vertical line on a plot surface.
+	/// </summary>
+	public class VerticalLine : IPlot
+	{
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="abscissaValue">abscissa (X) value of line.</param>
+		public VerticalLine( double abscissaValue )
+		{
+			this.value_ = abscissaValue;
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="abscissaValue">abscissa (X) value of line.</param>
+		/// <param name="color">draw the line using this color.</param>
+		public VerticalLine( double abscissaValue, Color color )
+		{
+			this.value_ = abscissaValue;
+			this.pen_ = new Pen( color );
+		}
+
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="abscissaValue">abscissa (X) value of line.</param>
+		/// <param name="pen">Pen to use to draw the line.</param>
+		public VerticalLine( double abscissaValue, Pen pen )
+		{
+			this.value_ = abscissaValue;
+			this.pen_ = pen;
+		}
+
+		/// <summary>
+		/// Draws a representation of the line in the legend
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw.</param>
+		/// <param name="startEnd">A rectangle specifying the bounds of the area in the legend set aside for drawing.</param>
+		public void DrawInLegend(System.Drawing.Graphics g, System.Drawing.Rectangle startEnd)
+		{
+			g.DrawLine( pen_, startEnd.Left, (startEnd.Top + startEnd.Bottom)/2, 
+				startEnd.Right, (startEnd.Top + startEnd.Bottom)/2 );
+		}
+
+
+		/// <summary>
+		/// A label to associate with the plot - used in the legend.
+		/// </summary>
+		public string Label
+		{
+			get
+			{
+				return label_;
+			}
+			set
+			{
+				this.label_ = value;
+			}
+		}
+		
+		private string label_ = "";
+
+
+		/// <summary>
+		/// Whether or not to include an entry for this plot in the legend if it exists.
+		/// </summary>
+		public bool ShowInLegend
+		{
+			get
+			{
+				return showInLegend_;
+			}
+			set
+			{
+				this.showInLegend_ = value;
+			}
+		}
+		private bool showInLegend_ = false;
+
+		/// <summary>
+		/// Returns an x-axis that is suitable for drawing this plot.
+		/// </summary>
+		/// <returns>A suitable x-axis.</returns>
+		public Axis SuggestXAxis()
+		{
+			return new LinearAxis( value_, value_ );
+		}
+
+
+		/// <summary>
+		/// Returns null indicating that y extremities of the line are variable.
+		/// </summary>
+		/// <returns>null</returns>
+		public Axis SuggestYAxis()
+		{
+			return null;
+		}
+
+		
+		/// <summary>
+		/// Writes text data describing the vertical line object to the supplied string builder. It is 
+		/// possible to specify that the data will be written only if the line is in the specified 
+		/// region.
+		/// </summary>
+		/// <param name="sb">the StringBuilder object to write to.</param>
+		/// <param name="region">a region used if onlyInRegion is true.</param>
+		/// <param name="onlyInRegion">If true, data will be written only if the line is in the specified region.</param>
+		public void WriteData(System.Text.StringBuilder sb, RectangleD region, bool onlyInRegion)
+		{
+
+			// return if line is not in plot region and 
+			if (value_ > region.X+region.Width || value_ < region.X)
+			{
+				if (onlyInRegion)
+				{
+					return;
+				}
+			}
+
+			sb.Append( "Label: " );
+			sb.Append( this.Label );
+			sb.Append( "\r\n" );
+			sb.Append( value_.ToString() );
+			sb.Append( "\r\n" );
+
+		}
+
+
+		/// <summary>
+		/// Draws the vertical line plot on a GDI+ surface against the provided x and y axes.
+		/// </summary>
+		/// <param name="g">The GDI+ surface on which to draw.</param>
+		/// <param name="xAxis">The X-Axis to draw against.</param>
+		/// <param name="yAxis">The Y-Axis to draw against.</param>
+		public void Draw(System.Drawing.Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis)
+		{
+			int yMin = yAxis.PhysicalMin.Y;
+			int yMax = yAxis.PhysicalMax.Y;
+			
+			yMin -= pixelIndent_;
+			yMax += pixelIndent_;
+
+			float length = Math.Abs(yMax - yMin);
+			float lengthDiff = length - length*scale_;
+			float indentAmount = lengthDiff/2;
+
+			yMin -= (int)indentAmount;
+			yMax += (int)indentAmount;
+
+			int xPos = (int)xAxis.WorldToPhysical( value_, false ).X;
+		
+			g.DrawLine( pen_, new System.Drawing.Point( xPos, yMin ), new System.Drawing.Point( xPos, yMax ) );
+
+			// todo:  clip and proper logic for flipped axis min max.
+		}
+
+
+		/// <summary>
+		/// abscissa (X) value to draw horizontal line at.
+		/// </summary>
+		public double AbscissaValue
+		{
+			get
+			{
+				return value_;
+			}
+			set
+			{
+				value_ = value;
+			}
+		}
+
+		/// <summary>
+		/// Pen to use to draw the horizontal line.
+		/// </summary>
+		public Pen Pen
+		{
+			get
+			{
+				return pen_;
+			}
+			set
+			{
+				pen_ = value;
+			}
+		}
+
+		
+		private double value_;
+		private Pen pen_ = new Pen( Color.Black );
+
+
+		/// <summary>
+		/// Each end of the line is indented by this many pixels. 
+		/// </summary>
+		public int PixelIndent
+		{
+			get
+			{
+				return pixelIndent_;
+			}
+			set
+			{
+				pixelIndent_ = value;
+			}
+		}
+		private int pixelIndent_ = 0;
+
+
+		/// <summary>
+		/// The line length is multiplied by this amount. Default
+		/// corresponds to a value of 1.0.
+		/// </summary>
+		public float LengthScale
+		{
+			get
+			{
+				return scale_;
+			}
+			set
+			{
+				scale_ = value;
+			}
+		}
+		private float scale_ = 1.0f;
+
+
+	}
+}
diff --git a/nplot/nplot/Web.Design.PlotSurface2D.cs b/nplot/nplot/Web.Design.PlotSurface2D.cs
new file mode 100644
index 0000000..1c26c38
--- /dev/null
+++ b/nplot/nplot/Web.Design.PlotSurface2D.cs
@@ -0,0 +1,121 @@
+/*
+NPlot - A charting library for .NET
+
+Web.PlotSurface2d.cs
+Copyright (C) 2003 
+Matt Howlett, Paolo Pierini
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using System.ComponentModel;
+using NPlot;
+
+namespace NPlot
+{
+	
+	namespace Web
+	{
+		namespace Design
+		{
+
+			/// <summary>
+			/// The Design Time rendered for the NPlot.web.PlotSurface2D control.
+			/// </summary>
+			public class PlotSurface2D : System.Web.UI.Design.ControlDesigner
+			{
+
+				/// <summary>
+				/// The design time generated HTML for the control.
+				/// </summary>
+				/// <returns>A string containing the HTML rendering.</returns>
+				public override string GetDesignTimeHtml()
+				{
+					
+					// Extremely simple design time rendering!
+					// will work on something better sooner or later.
+					// This acts as a placeholder.
+					Web.PlotSurface2D plot = (Web.PlotSurface2D)Component;
+
+					int xs = Convert.ToInt32(plot.Width.Value);
+					if ( xs < 1 ) return "";
+					int ys = Convert.ToInt32(plot.Height.Value);
+					if ( ys < 1 ) return "";
+
+					StringWriter sw = new StringWriter();
+					HtmlTextWriter output= new HtmlTextWriter(sw);
+					output.AddAttribute("border",plot.BorderWidth.ToString());
+					output.AddAttribute("borderColor",plot.BorderColor.ToKnownColor().ToString());
+					output.AddAttribute("cellSpacing","0");
+					output.AddAttribute("cellPadding","0");
+					output.AddAttribute("width",xs.ToString());
+					output.RenderBeginTag("table ");
+					output.RenderBeginTag("tr");
+					output.AddAttribute("vAlign","center");
+					output.AddAttribute("align","middle");
+					output.AddAttribute("height",ys.ToString());
+					output.RenderBeginTag("td");
+					output.RenderBeginTag("P");
+					output.Write("PlotSurface2D:" + plot.Title);
+					output.RenderEndTag();
+					output.RenderEndTag();
+					output.RenderEndTag();
+					output.RenderEndTag();
+					output.Flush();
+					return sw.ToString();
+				
+				}
+
+			}
+
+		}
+
+	}
+}
diff --git a/nplot/nplot/Web.PlotSurface2D.cs b/nplot/nplot/Web.PlotSurface2D.cs
new file mode 100644
index 0000000..58b7186
--- /dev/null
+++ b/nplot/nplot/Web.PlotSurface2D.cs
@@ -0,0 +1,669 @@
+/*
+NPlot - A charting library for .NET
+
+Web.PlotSurface2d.cs
+Copyright (C) 2003 
+Matt Howlett, Paolo Pierini
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Imaging;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using System.ComponentModel;
+using System.IO;
+using System.Collections;
+using System.Text;
+
+namespace NPlot
+{
+
+	namespace Web
+	{
+
+		/// <summary>
+		/// A PlotSurface2D web control. Rather than use this control, I generally create bitmaps
+		/// using Bitmap.PlotSurface2D, then use the ToBrowser() method in Bitmap.PlotSurface2D to
+		/// return them as a page request response (and point the src in an image tag to this page).
+		/// 
+		/// This is not as nice from a users perspective but is more efficient. 
+		/// 
+		/// Note: this control can chew up memory until the user session ends if the client cancels
+		/// the page load before the image has loaded. 
+		/// </summary>
+		[DefaultProperty("Title")] 
+		[ToolboxData("<{0}:PlotSurface2D runat=server></{0}:PlotSurface2D>")]
+		[Designer(typeof(NPlot.Web.Design.PlotSurface2D))]
+		public class PlotSurface2D : System.Web.UI.WebControls.WebControl, IPlotSurface2D 
+		{
+
+			private NPlot.PlotSurface2D ps_ = new NPlot.PlotSurface2D();
+
+
+			/// <summary>
+			/// Default constructor.
+			/// </summary>
+			public PlotSurface2D() : 
+				base()
+			{
+			}
+
+
+			/// <summary>
+			/// The URL to redirect for the plot.
+			/// </summary>
+			private string plotUrl;
+
+
+			/// <summary>
+			/// the prefix used for the session variables
+			/// </summary>
+			private string prefix()
+			{
+				string toReturn = "__PlotSurface2D_";
+				toReturn += this.ClientID;
+				toReturn += "_";
+				toReturn += this.Page.ToString();
+				toReturn += "_";
+				return toReturn;
+			}
+
+
+			/// <summary>
+			/// Clears the plot.
+			/// </summary>
+			public void Clear()
+			{
+				ps_.Clear();
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+			/// the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">The IDrawable object to add to the plot surface.</param>
+			public void Add( IDrawable p )
+			{
+				ps_.Add( p );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface against the specified axes. If
+			/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">the IDrawable object to add to the plot surface</param>
+			/// <param name="xp">the x-axis to add the plot against.</param>
+			/// <param name="yp">the y-axis to add the plot against.</param>
+			public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp, NPlot.PlotSurface2D.YAxisPosition yp )
+			{
+				ps_.Add( p, xp, yp );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+			/// the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">The IDrawable object to add to the plot surface.</param>
+			/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+			public void Add( IDrawable p, int zOrder )
+			{
+				ps_.Add( p, zOrder );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface against the specified axes. If
+			/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">the IDrawable object to add to the plot surface</param>
+			/// <param name="xp">the x-axis to add the plot against.</param>
+			/// <param name="yp">the y-axis to add the plot against.</param>
+			/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+			public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp,
+				NPlot.PlotSurface2D.YAxisPosition yp, int zOrder )
+			{
+				ps_.Add( p, xp, yp , zOrder);
+			}
+
+			/// <summary>
+			/// The plot surface title.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true)
+			]
+			public string Title
+			{
+				get 
+				{
+					return ps_.Title;
+				}
+				set 
+				{
+					ps_.Title = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The plot title font.
+			/// </summary>
+			[
+			Browsable(true)
+			]
+			public System.Drawing.Font TitleFont 
+			{
+				get 
+				{
+					return ps_.TitleFont;
+				}
+				set 
+				{
+					ps_.TitleFont = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The distance in pixels to leave between of the edge of the bounding rectangle
+			/// supplied to the Draw method, and the markings that make up the plot.
+			/// </summary>
+			[
+			Browsable(true),
+			Category("Data"),
+			Bindable(true)
+			]
+			public int Padding
+			{
+				get
+				{
+					return ps_.Padding;
+				}
+				set
+				{
+					ps_.Padding = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The first abscissa axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis XAxis1
+			{
+				get
+				{
+					return ps_.XAxis1;
+				}
+				set
+				{
+					ps_.XAxis1 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The first ordinate axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis YAxis1
+			{
+				get
+				{
+					return ps_.YAxis1;
+				}
+				set
+				{
+					ps_.YAxis1 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The second abscissa axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis XAxis2
+			{
+				get
+				{
+					return ps_.XAxis2;
+				}
+				set
+				{
+					ps_.XAxis2 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The second ordinate axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis YAxis2
+			{
+				get
+				{
+					return ps_.YAxis2;
+				}
+				set
+				{
+					ps_.YAxis2 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// A color used to paint the plot background. Mutually exclusive with PlotBackImage and PlotBackBrush
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public System.Drawing.Color PlotBackColor
+			{
+				set
+				{
+					ps_.PlotBackColor = value;
+				}
+			}
+
+
+			/// <summary>
+			/// An imaged used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+			/// </summary>
+			public System.Drawing.Bitmap PlotBackImage
+			{
+				set
+				{
+					ps_.PlotBackImage = value;
+				}
+			}
+
+
+			/// <summary>
+			/// A Rectangle brush used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+			/// </summary>
+			public IRectangleBrush PlotBackBrush
+			{
+				set
+				{
+					ps_.PlotBackBrush = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Gets or Sets the legend to use with this plot surface.
+			/// </summary>
+			[
+			Bindable(false),
+			Browsable(false)
+			]
+			public NPlot.Legend Legend
+			{
+				get
+				{
+					return ps_.Legend;
+				}
+				set
+				{
+					ps_.Legend = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Gets or Sets the legend z-order.
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public int LegendZOrder
+			{
+				get
+				{
+					return ps_.LegendZOrder;
+				}
+				set
+				{
+					ps_.LegendZOrder = value;
+				}
+			}
+	
+
+			/// <summary>
+			/// Smoothing mode to use when drawing plots.
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public System.Drawing.Drawing2D.SmoothingMode SmoothingMode 
+			{ 
+				get
+				{
+					return ps_.SmoothingMode;
+				}
+				set
+				{
+					ps_.SmoothingMode = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The bitmap background color outside the bounds of the plot surface.
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public override Color BackColor
+			{
+				set
+				{
+					backColor_ = value;
+				}
+			}
+			object backColor_ = null;
+			
+
+			/// <summary>
+			/// Ivan Ivanov wrote this function. From his email:
+			/// If the request string contains encoded parameters values [e.g. #  - %23]. 
+			/// The call to request.Url.ToString() will decode values [e.g. instead of %23 
+			/// it will return #]. On the subsequent request to the page that contains the 
+			/// nplot control [when the actual drawing of the image takes place] the request 
+			/// url will be cut up to the unformated value [e.g. #] and since the PlotSurface2D_ 
+			/// is added at the end of the query string, it will be missing.
+			/// </summary>
+			/// <returns></returns>
+			private String buildPlotURL()
+			{
+				StringBuilder urlParams = new StringBuilder();
+
+				foreach (string getParamName in Context.Request.QueryString.AllKeys)
+				{
+					urlParams.Append(getParamName + "=" +
+						Context.Server.UrlEncode(Context.Request.QueryString[getParamName]) + "&");
+				}
+
+				return Context.Request.Url.AbsolutePath +
+					(urlParams.Length > 0  ?
+					"?" + urlParams.Append("PlotSurface2D_" + this.ClientID + "=1").ToString() :
+					"?PlotSurface2D_" + this.ClientID + "=1");
+			}
+
+
+			/// <summary>
+			/// Initialization event.
+			/// </summary>
+			/// <param name="e"></param>
+			protected override void OnInit(EventArgs e)
+			{
+				System.Web.HttpRequest request = Context.Request;
+				System.Web.HttpResponse response = Context.Response;
+				if (request.Params["PlotSurface2D_" + this.ClientID] != null) 
+				{
+					// retrieve the bitmap and display
+					response.Clear();
+					try
+					{
+						response.ContentType = "Image/Png"; 
+						System.Drawing.Bitmap bmp = (System.Drawing.Bitmap) Context.Session[prefix()+"PNG"];
+                        
+                        // don't ask why, but if I write directly to the response
+						// I have a GDI+ error, if I first write to a MemoryStream and 
+						// then to the response.OutputStream I don't get an error.
+						System.IO.MemoryStream s = new System.IO.MemoryStream();
+						bmp.Save( s, System.Drawing.Imaging.ImageFormat.Png);
+						s.WriteTo(response.OutputStream);
+						Context.Session.Remove(prefix()+"PNG");
+					}
+					catch (Exception ex)
+					{
+						response.ContentType = "Text/HTML";
+						response.Write(	ex.Message );
+					}
+					finally
+					{
+						response.Flush();
+						response.End(); 
+					}
+				}
+				
+				this.plotUrl = this.buildPlotURL();
+				base.OnInit (e);
+			}
+
+	
+			/// <summary> 
+			/// Render this control as an HTML stream.
+			/// </summary>
+			/// <param name="output">The HTML writer to write out to.</param>
+			protected override void Render(HtmlTextWriter output)
+			{
+
+				// first of all render the bitmap;
+				System.Drawing.Bitmap b = new System.Drawing.Bitmap( (int)this.Width.Value, (int)this.Height.Value );
+				if (backColor_!=null)
+				{
+					Graphics g = Graphics.FromImage( b );
+					g.FillRectangle( (new Pen( (Color)this.backColor_)).Brush,0,0,b.Width,b.Height );
+				}
+				ps_.Draw( Graphics.FromImage(b), new System.Drawing.Rectangle(0,0,b.Width,b.Height) );
+
+				// then store in context memory. 
+				Context.Session[prefix()+"PNG"] = b;
+
+				// now render html.
+				if (this.BorderStyle == BorderStyle.None)
+				{
+					output.AddAttribute("border","0");
+				}
+				else
+				{
+					output.AddAttribute("border",this.BorderWidth.ToString());
+					output.AddAttribute("borderColor",this.BorderColor.ToKnownColor().ToString());
+				}
+				output.AddAttribute("cellSpacing","0");
+				output.AddAttribute("cellPadding","0");
+				output.RenderBeginTag("table");
+				output.RenderBeginTag("tr");
+				output.AddAttribute("vAlign","center");
+				output.AddAttribute("align","middle");
+				output.RenderBeginTag("td");
+				output.RenderBeginTag("P");
+				output.AddAttribute("src",this.plotUrl);
+				output.AddAttribute("alt",this.ToolTip);
+				output.RenderBeginTag("img");
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.Flush();
+			}
+
+			/// <summary>
+			/// Add an axis constraint to the plot surface. Axis constraints can
+			/// specify relative world-pixel scalings, absolute axis positions etc.
+			/// </summary>
+			/// <param name="c">The axis constraint to add.</param>
+			public void AddAxesConstraint( AxesConstraint c )
+			{
+				ps_.AddAxesConstraint( c );
+			}
+
+
+			/// <summary>
+			/// Whether or not the title will be scaled according to size of the plot 
+			/// surface.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true)
+			]
+			public bool AutoScaleTitle
+			{
+				get
+				{
+					return ps_.AutoScaleTitle;
+				}
+				set
+				{
+					ps_.AutoScaleTitle = value;
+				}
+			}
+
+
+			/// <summary>
+			/// When plots are added to the plot surface, the axes they are attached to
+			/// are immediately modified to reflect data of the plot. If 
+			/// AutoScaleAutoGeneratedAxes is true when a plot is added, the axes will
+			/// be turned in to auto scaling ones if they are not already [tick marks,
+			/// tick text and label size scaled to size of plot surface]. If false,
+			/// axes will not be autoscaling.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true),
+			]
+			public bool AutoScaleAutoGeneratedAxes
+			{
+				get
+				{
+					return ps_.AutoScaleAutoGeneratedAxes;
+				}
+				set
+				{
+					ps_.AutoScaleAutoGeneratedAxes = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Sets the title to be drawn using a solid brush of this color.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true)
+			]
+			public Color TitleColor
+			{
+				set
+				{
+					ps_.TitleColor = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The brush used for drawing the title.
+			/// </summary>
+			[
+			Browsable(false)
+			]
+			public Brush TitleBrush
+			{
+				get
+				{
+					return ps_.TitleBrush;
+				}
+				set
+				{
+					ps_.TitleBrush = value;
+				}
+			}
+
+			/// <summary>
+			/// Remove a drawable object from the plot surface.
+			/// </summary>
+			/// <param name="p">the drawable to remove</param>
+			/// <param name="updateAxes">whether or not to update the axes after removing the idrawable.</param>
+			public void Remove( IDrawable p, bool updateAxes ) 
+			{
+				ps_.Remove( p, updateAxes );
+			}
+
+
+			/// <summary>
+			/// Gets an array list containing all drawables currently added to the PlotSurface2D.
+			/// </summary>
+			[
+			Browsable(false)
+			]
+			public ArrayList Drawables
+			{
+				get
+				{
+					return ps_.Drawables;
+				}
+			}
+
+		}
+	}
+
+}
diff --git a/nplot/nplot/Web.PlotSurface2D.cs~ b/nplot/nplot/Web.PlotSurface2D.cs~
new file mode 100644
index 0000000..1250fc7
--- /dev/null
+++ b/nplot/nplot/Web.PlotSurface2D.cs~
@@ -0,0 +1,671 @@
+/*
+NPlot - A charting library for .NET
+
+Web.PlotSurface2d.cs
+Copyright (C) 2003 
+Matt Howlett, Paolo Pierini
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	    on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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;
+using System.Drawing.Imaging;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using System.ComponentModel;
+using System.IO;
+using System.Collections;
+using System.Text;
+
+namespace NPlot
+{
+
+	namespace Web
+	{
+
+		/// <summary>
+		/// A PlotSurface2D web control. Rather than use this control, I generally create bitmaps
+		/// using Bitmap.PlotSurface2D, then use the ToBrowser() method in Bitmap.PlotSurface2D to
+		/// return them as a page request response (and point the src in an image tag to this page).
+		/// 
+		/// This is not as nice from a users perspective but is more efficient. 
+		/// 
+		/// Note: this control can chew up memory until the user session ends if the client cancels
+		/// the page load before the image has loaded. 
+		/// </summary>
+		[
+		DefaultProperty("Title"), 
+		ToolboxData("<{0}:PlotSurface2D runat=server></{0}:PlotSurface2D>"),
+		Designer(typeof(NPlot.Web.Design.PlotSurface2D))
+		]
+		public class PlotSurface2D : System.Web.UI.WebControls.WebControl, IPlotSurface2D 
+		{
+
+			private NPlot.PlotSurface2D ps_ = new NPlot.PlotSurface2D();
+
+
+			/// <summary>
+			/// Default constructor.
+			/// </summary>
+			public PlotSurface2D() : 
+				base()
+			{
+			}
+
+
+			/// <summary>
+			/// The URL to redirect for the plot.
+			/// </summary>
+			private string plotUrl;
+
+
+			/// <summary>
+			/// the prefix used for the session variables
+			/// </summary>
+			private string prefix()
+			{
+				string toReturn = "__PlotSurface2D_";
+				toReturn += this.ClientID;
+				toReturn += "_";
+				toReturn += this.Page.ToString();
+				toReturn += "_";
+				return toReturn;
+			}
+
+
+			/// <summary>
+			/// Clears the plot.
+			/// </summary>
+			public void Clear()
+			{
+				ps_.Clear();
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+			/// the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">The IDrawable object to add to the plot surface.</param>
+			public void Add( IDrawable p )
+			{
+				ps_.Add( p );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface against the specified axes. If
+			/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">the IDrawable object to add to the plot surface</param>
+			/// <param name="xp">the x-axis to add the plot against.</param>
+			/// <param name="yp">the y-axis to add the plot against.</param>
+			public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp, NPlot.PlotSurface2D.YAxisPosition yp )
+			{
+				ps_.Add( p, xp, yp );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+			/// the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">The IDrawable object to add to the plot surface.</param>
+			/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+			public void Add( IDrawable p, int zOrder )
+			{
+				ps_.Add( p, zOrder );
+			}
+
+
+			/// <summary>
+			/// Adds a drawable object to the plot surface against the specified axes. If
+			/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+			/// </summary>
+			/// <param name="p">the IDrawable object to add to the plot surface</param>
+			/// <param name="xp">the x-axis to add the plot against.</param>
+			/// <param name="yp">the y-axis to add the plot against.</param>
+			/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+			public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp,
+				NPlot.PlotSurface2D.YAxisPosition yp, int zOrder )
+			{
+				ps_.Add( p, xp, yp , zOrder);
+			}
+
+			/// <summary>
+			/// The plot surface title.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true)
+			]
+			public string Title
+			{
+				get 
+				{
+					return ps_.Title;
+				}
+				set 
+				{
+					ps_.Title = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The plot title font.
+			/// </summary>
+			[
+			Browsable(true)
+			]
+			public System.Drawing.Font TitleFont 
+			{
+				get 
+				{
+					return ps_.TitleFont;
+				}
+				set 
+				{
+					ps_.TitleFont = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The distance in pixels to leave between of the edge of the bounding rectangle
+			/// supplied to the Draw method, and the markings that make up the plot.
+			/// </summary>
+			[
+			Browsable(true),
+			Category("Data"),
+			Bindable(true)
+			]
+			public int Padding
+			{
+				get
+				{
+					return ps_.Padding;
+				}
+				set
+				{
+					ps_.Padding = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The first abscissa axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis XAxis1
+			{
+				get
+				{
+					return ps_.XAxis1;
+				}
+				set
+				{
+					ps_.XAxis1 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The first ordinate axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis YAxis1
+			{
+				get
+				{
+					return ps_.YAxis1;
+				}
+				set
+				{
+					ps_.YAxis1 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The second abscissa axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis XAxis2
+			{
+				get
+				{
+					return ps_.XAxis2;
+				}
+				set
+				{
+					ps_.XAxis2 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The second ordinate axis.
+			/// </summary>
+			[
+			Browsable(false),
+			Bindable(false)
+			]
+			public Axis YAxis2
+			{
+				get
+				{
+					return ps_.YAxis2;
+				}
+				set
+				{
+					ps_.YAxis2 = value;
+				}
+			}
+
+
+			/// <summary>
+			/// A color used to paint the plot background. Mutually exclusive with PlotBackImage and PlotBackBrush
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public System.Drawing.Color PlotBackColor
+			{
+				set
+				{
+					ps_.PlotBackColor = value;
+				}
+			}
+
+
+			/// <summary>
+			/// An imaged used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+			/// </summary>
+			public System.Drawing.Bitmap PlotBackImage
+			{
+				set
+				{
+					ps_.PlotBackImage = value;
+				}
+			}
+
+
+			/// <summary>
+			/// A Rectangle brush used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+			/// </summary>
+			public IRectangleBrush PlotBackBrush
+			{
+				set
+				{
+					ps_.PlotBackBrush = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Gets or Sets the legend to use with this plot surface.
+			/// </summary>
+			[
+			Bindable(false),
+			Browsable(false)
+			]
+			public NPlot.Legend Legend
+			{
+				get
+				{
+					return ps_.Legend;
+				}
+				set
+				{
+					ps_.Legend = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Gets or Sets the legend z-order.
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public int LegendZOrder
+			{
+				get
+				{
+					return ps_.LegendZOrder;
+				}
+				set
+				{
+					ps_.LegendZOrder = value;
+				}
+			}
+	
+
+			/// <summary>
+			/// Smoothing mode to use when drawing plots.
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public System.Drawing.Drawing2D.SmoothingMode SmoothingMode 
+			{ 
+				get
+				{
+					return ps_.SmoothingMode;
+				}
+				set
+				{
+					ps_.SmoothingMode = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The bitmap background color outside the bounds of the plot surface.
+			/// </summary>
+			[
+			Bindable(true),
+			Browsable(true)
+			]
+			public override Color BackColor
+			{
+				set
+				{
+					backColor_ = value;
+				}
+			}
+			object backColor_ = null;
+			
+
+			/// <summary>
+			/// Ivan Ivanov wrote this function. From his email:
+			/// If the request string contains encoded parameters values [e.g. #  - %23]. 
+			/// The call to request.Url.ToString() will decode values [e.g. instead of %23 
+			/// it will return #]. On the subsequent request to the page that contains the 
+			/// nplot control [when the actual drawing of the image takes place] the request 
+			/// url will be cut up to the unformated value [e.g. #] and since the PlotSurface2D_ 
+			/// is added at the end of the query string, it will be missing.
+			/// </summary>
+			/// <returns></returns>
+			private String buildPlotURL()
+			{
+				StringBuilder urlParams = new StringBuilder();
+
+				foreach (string getParamName in Context.Request.QueryString.AllKeys)
+				{
+					urlParams.Append(getParamName + "=" +
+						Context.Server.UrlEncode(Context.Request.QueryString[getParamName]) + "&");
+				}
+
+				return Context.Request.Url.AbsolutePath +
+					(urlParams.Length > 0  ?
+					"?" + urlParams.Append("PlotSurface2D_" + this.ClientID + "=1").ToString() :
+					"?PlotSurface2D_" + this.ClientID + "=1");
+			}
+
+
+			/// <summary>
+			/// Initialization event.
+			/// </summary>
+			/// <param name="e"></param>
+			protected override void OnInit(EventArgs e)
+			{
+				System.Web.HttpRequest request = Context.Request;
+				System.Web.HttpResponse response = Context.Response;
+				if (request.Params["PlotSurface2D_" + this.ClientID] != null) 
+				{
+					// retrieve the bitmap and display
+					response.Clear();
+					try
+					{
+						response.ContentType = "Image/Png"; 
+						System.Drawing.Bitmap bmp = (System.Drawing.Bitmap) Context.Session[prefix()+"PNG"];
+                        
+                        // don't ask why, but if I write directly to the response
+						// I have a GDI+ error, if I first write to a MemoryStream and 
+						// then to the response.OutputStream I don't get an error.
+						System.IO.MemoryStream s = new System.IO.MemoryStream();
+						bmp.Save( s, System.Drawing.Imaging.ImageFormat.Png);
+						s.WriteTo(response.OutputStream);
+						Context.Session.Remove(prefix()+"PNG");
+					}
+					catch (Exception ex)
+					{
+						response.ContentType = "Text/HTML";
+						response.Write(	ex.Message );
+					}
+					finally
+					{
+						response.Flush();
+						response.End(); 
+					}
+				}
+				
+				this.plotUrl = this.buildPlotURL();
+				base.OnInit (e);
+			}
+
+	
+			/// <summary> 
+			/// Render this control as an HTML stream.
+			/// </summary>
+			/// <param name="output">The HTML writer to write out to.</param>
+			protected override void Render(HtmlTextWriter output)
+			{
+
+				// first of all render the bitmap;
+				System.Drawing.Bitmap b = new System.Drawing.Bitmap( (int)this.Width.Value, (int)this.Height.Value );
+				if (backColor_!=null)
+				{
+					Graphics g = Graphics.FromImage( b );
+					g.FillRectangle( (new Pen( (Color)this.backColor_)).Brush,0,0,b.Width,b.Height );
+				}
+				ps_.Draw( Graphics.FromImage(b), new System.Drawing.Rectangle(0,0,b.Width,b.Height) );
+
+				// then store in context memory. 
+				Context.Session[prefix()+"PNG"] = b;
+
+				// now render html.
+				if (this.BorderStyle == BorderStyle.None)
+				{
+					output.AddAttribute("border","0");
+				}
+				else
+				{
+					output.AddAttribute("border",this.BorderWidth.ToString());
+					output.AddAttribute("borderColor",this.BorderColor.ToKnownColor().ToString());
+				}
+				output.AddAttribute("cellSpacing","0");
+				output.AddAttribute("cellPadding","0");
+				output.RenderBeginTag("table");
+				output.RenderBeginTag("tr");
+				output.AddAttribute("vAlign","center");
+				output.AddAttribute("align","middle");
+				output.RenderBeginTag("td");
+				output.RenderBeginTag("P");
+				output.AddAttribute("src",this.plotUrl);
+				output.AddAttribute("alt",this.ToolTip);
+				output.RenderBeginTag("img");
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.RenderEndTag();
+				output.Flush();
+			}
+
+			/// <summary>
+			/// Add an axis constraint to the plot surface. Axis constraints can
+			/// specify relative world-pixel scalings, absolute axis positions etc.
+			/// </summary>
+			/// <param name="c">The axis constraint to add.</param>
+			public void AddAxesConstraint( AxesConstraint c )
+			{
+				ps_.AddAxesConstraint( c );
+			}
+
+
+			/// <summary>
+			/// Whether or not the title will be scaled according to size of the plot 
+			/// surface.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true)
+			]
+			public bool AutoScaleTitle
+			{
+				get
+				{
+					return ps_.AutoScaleTitle;
+				}
+				set
+				{
+					ps_.AutoScaleTitle = value;
+				}
+			}
+
+
+			/// <summary>
+			/// When plots are added to the plot surface, the axes they are attached to
+			/// are immediately modified to reflect data of the plot. If 
+			/// AutoScaleAutoGeneratedAxes is true when a plot is added, the axes will
+			/// be turned in to auto scaling ones if they are not already [tick marks,
+			/// tick text and label size scaled to size of plot surface]. If false,
+			/// axes will not be autoscaling.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true),
+			]
+			public bool AutoScaleAutoGeneratedAxes
+			{
+				get
+				{
+					return ps_.AutoScaleAutoGeneratedAxes;
+				}
+				set
+				{
+					ps_.AutoScaleAutoGeneratedAxes = value;
+				}
+			}
+
+
+			/// <summary>
+			/// Sets the title to be drawn using a solid brush of this color.
+			/// </summary>
+			[
+			Browsable(true),
+			Bindable(true)
+			]
+			public Color TitleColor
+			{
+				set
+				{
+					ps_.TitleColor = value;
+				}
+			}
+
+
+			/// <summary>
+			/// The brush used for drawing the title.
+			/// </summary>
+			[
+			Browsable(false)
+			]
+			public Brush TitleBrush
+			{
+				get
+				{
+					return ps_.TitleBrush;
+				}
+				set
+				{
+					ps_.TitleBrush = value;
+				}
+			}
+
+			/// <summary>
+			/// Remove a drawable object from the plot surface.
+			/// </summary>
+			/// <param name="p">the drawable to remove</param>
+			/// <param name="updateAxes">whether or not to update the axes after removing the idrawable.</param>
+			public void Remove( IDrawable p, bool updateAxes ) 
+			{
+				ps_.Remove( p, updateAxes );
+			}
+
+
+			/// <summary>
+			/// Gets an array list containing all drawables currently added to the PlotSurface2D.
+			/// </summary>
+			[
+			Browsable(false)
+			]
+			public ArrayList Drawables
+			{
+				get
+				{
+					return ps_.Drawables;
+				}
+			}
+
+		}
+	}
+
+}
diff --git a/nplot/nplot/Windows.PlotSurface.cs b/nplot/nplot/Windows.PlotSurface.cs
new file mode 100644
index 0000000..ffd839c
--- /dev/null
+++ b/nplot/nplot/Windows.PlotSurface.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+
+namespace NPlot.Windows
+{
+
+	/// <summary>
+	/// An all encompasing Windows.Forms PlotSurface. This class allows you
+	/// to place a single control in your form layout and draw to any type of 
+	/// plot surface (PlotSurface2D, PlotSurface3D etc) on it. As there is only
+	///  one type of plot surface currently, this 
+	/// class isn't necessary... but more a planned soon. 
+	/// Also, the implementation isn't finished.
+	/// </summary>
+	public class PlotSurface : System.Windows.Forms.UserControl
+	{
+
+		/// <summary>
+		/// control resize handler.
+		/// </summary>
+		/// <param name="e">event args.</param>
+		protected override void OnResize( EventArgs e )
+		{
+			this.Refresh();
+			base.OnResize(e);
+		}
+
+
+		/// <summary>
+		/// control paint handler.
+		/// </summary>
+		/// <param name="pe">paint event args.</param>
+		protected override void OnPaint( PaintEventArgs pe )
+		{
+			surface_.DoPaint( pe, this.Width, this.Height );
+			base.OnPaint(pe);
+		}
+
+		
+		/// <summary>
+		/// Mouse down event handler.
+		/// </summary>
+		/// <param name="e">mouse event args</param>
+		protected override void OnMouseDown(MouseEventArgs e)
+		{
+			surface_.DoMouseDown(e);
+			base.OnMouseDown(e);
+		}
+
+
+		/// <summary>
+		/// Mouse Up event handler.
+		/// </summary>
+		/// <param name="e">mouse event args.</param>
+		protected override void OnMouseUp(MouseEventArgs e)
+		{
+			surface_.DoMouseUp(e, this);
+			base.OnMouseUp(e);
+		}
+
+
+		/// <summary>
+		/// Mouse Move event handler.
+		/// </summary>
+		/// <param name="e">mouse event args.</param>
+		protected override void OnMouseMove(MouseEventArgs e)
+		{
+			surface_.DoMouseMove(e, this);
+			base.OnMouseMove(e);
+		}
+
+		/*
+		enum Type 
+		{
+			PlotSurface2D,
+			PlotSurface2Dnew,
+			PlotSurface3D,
+			PieChart
+		}
+		*/
+
+		ISurface surface_ = null;
+		/// <summary>
+		/// Gets the underlying plot surface.
+		/// </summary>
+		public ISurface Surface
+		{
+			get
+			{
+				return surface_;
+			}
+		}
+
+		/// <summary>
+		/// Constructor.
+		/// </summary>
+		public PlotSurface()
+		{
+			// double buffer, and update when resize.
+			base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+			base.SetStyle(ControlStyles.DoubleBuffer, true);
+			base.SetStyle(ControlStyles.UserPaint, true);
+			base.ResizeRedraw = true;
+
+			surface_ = new NPlot.Windows.PlotSurface2D();
+		}
+
+	}
+}
diff --git a/nplot/nplot/Windows.PlotSurface.resx b/nplot/nplot/Windows.PlotSurface.resx
new file mode 100644
index 0000000..dd0ea4d
--- /dev/null
+++ b/nplot/nplot/Windows.PlotSurface.resx
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<root>
+	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"; xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+		<xsd:element name="root" msdata:IsDataSet="true">
+			<xsd:complexType>
+				<xsd:choice maxOccurs="unbounded">
+					<xsd:element name="data">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+							</xsd:sequence>
+							<xsd:attribute name="name" type="xsd:string" />
+							<xsd:attribute name="type" type="xsd:string" />
+							<xsd:attribute name="mimetype" type="xsd:string" />
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="resheader">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+							</xsd:sequence>
+							<xsd:attribute name="name" type="xsd:string" use="required" />
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:choice>
+			</xsd:complexType>
+		</xsd:element>
+	</xsd:schema>
+	<resheader name="ResMimeType">
+		<value>text/microsoft-resx</value>
+	</resheader>
+	<resheader name="Version">
+		<value>1.0.0.0</value>
+	</resheader>
+	<resheader name="Reader">
+		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+	</resheader>
+	<resheader name="Writer">
+		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+	</resheader>
+</root>
diff --git a/nplot/nplot/Windows.PlotSurface2D.cs b/nplot/nplot/Windows.PlotSurface2D.cs
new file mode 100644
index 0000000..4337238
--- /dev/null
+++ b/nplot/nplot/Windows.PlotSurface2D.cs
@@ -0,0 +1,2942 @@
+/*
+NPlot - A charting library for .NET
+
+Windows.PlotSurface2d.cs
+Copyright (C) 2003 
+Matt Howlett
+
+Redistribution and use of NPlot or parts there-of in source and
+binary forms, with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Re-distributions in source form must retain at the head of each
+   source file the above copyright notice, this list of conditions
+   and the following disclaimer.
+
+2. Any product ("the product") that makes use NPlot or parts 
+   there-of must either:
+  
+    (a) allow any user of the product to obtain a complete machine-
+        readable copy of the corresponding source code for the 
+        product and the version of NPlot used for a charge no more
+        than your cost of physically performing source distribution,
+	on a medium customarily used for software interchange, or:
+
+    (b) reproduce the following text in the documentation, about 
+        box or other materials intended to be read by human users
+        of the product that is provided to every human user of the
+        product: 
+   
+              "This product includes software developed as 
+              part of the NPlot library project available 
+              from: http://www.nplot.com/"; 
+
+        The words "This product" may optionally be replace with 
+        the actual name of the product.
+
+------------------------------------------------------------------------
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+using System.Drawing.Printing;
+
+using NPlot;
+
+namespace NPlot.Windows
+{
+
+	/// <summary>
+	/// A Windows.Forms PlotSurface2D control.
+	/// </summary>
+	/// <remarks>
+	/// Unfortunately it's not possible to derive from both Control and NPlot.PlotSurface2D.
+	/// </remarks>
+	[ ToolboxBitmapAttribute(typeof(NPlot.Windows.PlotSurface2D),"PlotSurface2D.ico") ]
+	public class PlotSurface2D : System.Windows.Forms.Control, IPlotSurface2D, ISurface
+	{
+
+        private System.Windows.Forms.ToolTip coordinates_;
+
+		private System.Collections.ArrayList selectedObjects_;
+        private NPlot.PlotSurface2D ps_;
+
+		private Axis xAxis1ZoomCache_;
+		private Axis yAxis1ZoomCache_;
+		private Axis xAxis2ZoomCache_;
+		private Axis yAxis2ZoomCache_;
+
+        /// <summary>
+		/// Flag to display a coordinates in a tooltip.
+		/// </summary>
+		[ 
+		Category("PlotSurface2D"),
+		Description("Whether or not to show coordinates in a tool tip when the mouse hovers above the plot area."),
+		Browsable(true),
+		Bindable(true)
+		]
+		public bool ShowCoordinates
+		{
+			get
+			{
+				return this.coordinates_.Active;
+			}
+			set
+			{
+				this.coordinates_.Active = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Default constructor.
+		/// </summary>
+		public PlotSurface2D()
+		{
+			// This call is required by the Windows.Forms Form Designer.
+			InitializeComponent();
+
+            // double buffer, and update when resize.
+			base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+			base.SetStyle(ControlStyles.DoubleBuffer, true);
+			base.SetStyle(ControlStyles.UserPaint, true);
+			base.ResizeRedraw = true;
+
+			ps_ = new NPlot.PlotSurface2D();
+
+            this.InteractionOccured += new InteractionHandler( OnInteractionOccured );
+            this.PreRefresh += new PreRefreshHandler( OnPreRefresh );
+		}
+
+
+		/// <summary> 
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		/// <remarks>Modified! :-)</remarks>
+		private void InitializeComponent()
+		{
+			this.components = new System.ComponentModel.Container();
+			this.coordinates_ = new System.Windows.Forms.ToolTip(this.components);
+			// 
+			// PlotSurface2D
+			// 
+			this.BackColor = System.Drawing.SystemColors.ControlLightLight;
+			this.Size = new System.Drawing.Size(328, 272);
+
+		}
+
+
+        KeyEventArgs lastKeyEventArgs_ = null;
+        /// <summary>
+        /// the key down callback
+        /// </summary>
+        /// <param name="e">information pertaining to the event</param>
+        protected override void OnKeyDown(KeyEventArgs e)
+        {
+            lastKeyEventArgs_ = e;
+        }
+
+        /// <summary>
+        /// The key up callback.
+        /// </summary>
+        /// <param name="e">information pertaining to the event</param>
+        protected override void OnKeyUp(KeyEventArgs e)
+        {
+            lastKeyEventArgs_ = e;
+        }
+
+        /// <summary>
+		/// the paint event callback.
+		/// </summary>
+		/// <param name="pe">the PaintEventArgs</param>
+		protected override void OnPaint( PaintEventArgs pe )
+		{
+			DoPaint( pe, this.Width, this.Height );
+			base.OnPaint(pe);
+		}
+
+
+		/// <summary>
+		/// All functionality of the OnPaint method is provided by this function.
+		/// This allows use of the all encompasing PlotSurface.
+		/// </summary>
+		/// <param name="pe">the PaintEventArgs from paint event.</param>
+		/// <param name="width">width of the control</param>
+		/// <param name="height">height of the control</param>
+		public void DoPaint( PaintEventArgs pe, int width, int height )
+		{
+            this.PreRefresh(this);
+
+            foreach (Interactions.Interaction i in interactions_)
+            {
+                i.DoPaint(pe,width,height);
+            }
+
+            /*
+            // make sure don't redraw after a refresh.
+            this.horizontalBarPos_ = -1;
+            this.verticalBarPos_ = -1;
+            */
+
+            Graphics g = pe.Graphics;
+			
+			Rectangle border = new Rectangle( 0, 0, width, height );
+
+			if ( g == null ) 
+			{
+				throw (new NPlotException("null graphics context!"));
+			}
+			
+			if ( ps_ == null )
+			{
+				throw (new NPlotException("null NPlot.PlotSurface2D"));
+			}
+			
+			if ( border == Rectangle.Empty )
+			{
+				throw (new NPlotException("null border context"));
+			}
+
+			this.Draw( g, border );
+		}
+
+
+		/// <summary>
+		/// Draws the plot surface on the supplied graphics surface [not the control surface].
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw</param>
+		/// <param name="bounds">A bounding box on this surface that denotes the area on the
+		/// surface to confine drawing to.</param>
+		public void Draw( Graphics g, Rectangle bounds )
+		{
+
+			// If we are not in design mode then draw as normal.
+			if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) 
+			{ 
+				this.drawDesignMode( g, bounds );
+			}
+
+			ps_.Draw( g, bounds );
+		
+		}
+
+
+		/// <summary>
+		/// Draw a lightweight representation of us for design mode.
+		/// </summary>
+		private void drawDesignMode( Graphics g, Rectangle bounds )
+		{
+			g.DrawRectangle( new Pen(Color.Black), bounds.X + 2, bounds.Y + 2, bounds.Width-4, bounds.Height-4 );
+			g.DrawString( "PlotSurface2D: " + this.Title, this.TitleFont, this.TitleBrush, bounds.X + bounds.Width/2.0f, bounds.Y + bounds.Height/2.0f );
+		}
+
+
+		/// <summary>
+		/// Clears the plot and resets to default values.
+		/// </summary>
+		public void Clear()
+		{
+			xAxis1ZoomCache_ = null;
+			yAxis1ZoomCache_ = null;
+			xAxis2ZoomCache_ = null;
+			yAxis2ZoomCache_ = null;
+			ps_.Clear();
+            interactions_.Clear();
+        }
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+		/// the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">The IDrawable object to add to the plot surface.</param>
+		public void Add( IDrawable p )
+		{
+			ps_.Add( p );
+		}
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface against the specified axes. If
+		/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">the IDrawable object to add to the plot surface</param>
+		/// <param name="xp">the x-axis to add the plot against.</param>
+		/// <param name="yp">the y-axis to add the plot against.</param>
+		public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp, NPlot.PlotSurface2D.YAxisPosition yp )
+		{
+			ps_.Add( p, xp, yp );
+		}
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface. If the object is an IPlot, 
+		/// the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">The IDrawable object to add to the plot surface.</param>
+		/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+		public void Add( IDrawable p, int zOrder )
+		{
+			ps_.Add( p, zOrder );
+		}
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface against the specified axes. If
+		/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">the IDrawable object to add to the plot surface</param>
+		/// <param name="xp">the x-axis to add the plot against.</param>
+		/// <param name="yp">the y-axis to add the plot against.</param>
+		/// <param name="zOrder">The z-ordering when drawing (objects with lower numbers are drawn first)</param>
+		public void Add( IDrawable p, NPlot.PlotSurface2D.XAxisPosition xp,
+			NPlot.PlotSurface2D.YAxisPosition yp, int zOrder )
+		{
+			ps_.Add( p, xp, yp , zOrder);
+		}
+
+
+		/// <summary>
+		/// Gets or Sets the legend to use with this plot surface.
+		/// </summary>
+		[
+		Browsable(false),
+		Bindable(false)
+		]
+		public NPlot.Legend Legend
+		{
+			get
+			{
+				return ps_.Legend;
+			}
+			set
+			{
+				ps_.Legend = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Gets or Sets the legend z-order.
+		/// </summary>
+		[
+		Browsable(true),
+		Bindable(true),
+		Category("PlotSurface2D"),
+		Description("Determines the order with respect to other IDrawables on the plot surface in which the legend is drawn. " +
+			"The higher this value, the higher the position in the draw order." )
+		]
+		public int LegendZOrder
+		{
+			get
+			{
+				return ps_.LegendZOrder;
+			}
+			set
+			{
+				ps_.LegendZOrder = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Whether or not the title will be scaled according to size of the plot 
+		/// surface.
+		/// </summary>
+		[
+		Browsable(true),
+		Bindable(true),
+		Description("Whether or not the title will be scaled according to size of the plot surface."),
+		Category("PlotSurface2D")
+		]
+		public bool AutoScaleTitle
+		{
+			get
+			{
+				return ps_.AutoScaleTitle;
+			}
+			set
+			{
+				ps_.AutoScaleTitle = value;
+			}
+		}
+
+
+		/// <summary>
+		/// When plots are added to the plot surface, the axes they are attached to
+		/// are immediately modified to reflect data of the plot. If 
+		/// AutoScaleAutoGeneratedAxes is true when a plot is added, the axes will
+		/// be turned in to auto scaling ones if they are not already [tick marks,
+		/// tick text and label size scaled to size of plot surface]. If false,
+		/// axes will not be autoscaling.
+		/// </summary>
+		[
+		Browsable(true),
+		Bindable(true),
+		Description( "When plots are added to the plot surface, the axes they are attached to are immediately modified " +
+			"to reflect data of the plot. If AutoScaleAutoGeneratedAxes is true when a plot is added, the axes will be " +
+			"turned in to auto scaling ones if they are not already [tick marks, tick text and label size scaled to size " +
+			"of plot surface]. If false, axes will not be autoscaling." ),
+		Category("PlotSurface2D")
+		]
+		public bool AutoScaleAutoGeneratedAxes
+		{
+			get
+			{
+				return ps_.AutoScaleAutoGeneratedAxes;
+			}
+			set
+			{
+				ps_.AutoScaleAutoGeneratedAxes = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The plot surface title.
+		/// </summary>
+		[
+		Category("PlotSurface2D"),
+		Description("The plot surface title"),
+		Browsable(true),
+		Bindable(true)
+		]
+		public string Title
+		{
+			get 
+			{
+				return ps_.Title;
+			}
+			set 
+			{
+				ps_.Title = value;
+				//helpful in design view. But crap in applications!
+				//this.Refresh();
+			}
+		}
+
+
+		/// <summary>
+		/// The font used to draw the title.
+		/// </summary>
+		[
+		Category("PlotSurface2D"),
+		Description("The font used to draw the title."),
+		Browsable(true),
+		Bindable(false)
+		]
+		public Font TitleFont 
+		{
+			get 
+			{
+				return ps_.TitleFont;
+			}
+			set 
+			{
+				ps_.TitleFont = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Padding of this width will be left between what is drawn and the control border.
+		/// </summary>
+		[
+		Category("PlotSurface2D"),
+		Description("Padding of this width will be left between what is drawn and the control border."),
+		Browsable(true),
+		Bindable(true)
+		]
+		public int Padding
+		{
+			get
+			{
+				return ps_.Padding;
+			}
+			set
+			{
+				ps_.Padding = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The first abscissa axis.
+		/// </summary>
+		/// 
+		[
+		Browsable(false)
+		]
+		public Axis XAxis1
+		{
+			get
+			{
+				return ps_.XAxis1;
+			}
+			set
+			{
+				ps_.XAxis1 = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The first ordinate axis.
+		/// </summary>
+		[
+		Browsable(false)
+		]
+		public Axis YAxis1
+		{
+			get
+			{
+				return ps_.YAxis1;
+			}
+			set
+			{
+				ps_.YAxis1 = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The second abscissa axis.
+		/// </summary>
+		[
+		Browsable(false)
+		]
+		public Axis XAxis2
+		{
+			get
+			{
+				return ps_.XAxis2;
+			}
+			set
+			{
+				ps_.XAxis2 = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The second ordinate axis.
+		/// </summary>
+		[
+		Browsable(false)
+		]
+		public Axis YAxis2
+		{
+			get
+			{
+				return ps_.YAxis2;
+			}
+			set
+			{
+				ps_.YAxis2 = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical XAxis1 that was last drawn.
+		/// </summary>
+		[
+		Browsable(false)
+		]
+		public PhysicalAxis PhysicalXAxis1Cache
+		{
+			get
+			{
+				return ps_.PhysicalXAxis1Cache;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical YAxis1 that was last drawn.
+		/// </summary>
+		[
+		Browsable(false)
+		]
+		public PhysicalAxis PhysicalYAxis1Cache
+		{
+			get
+			{
+				return ps_.PhysicalYAxis1Cache;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical XAxis2 that was last drawn.
+		/// </summary>
+		[
+		Browsable(false)
+		]
+		public PhysicalAxis PhysicalXAxis2Cache
+		{
+			get
+			{
+				return ps_.PhysicalXAxis2Cache;
+			}
+		}
+
+
+		/// <summary>
+		/// The physical YAxis2 that was last drawn.
+		/// </summary>
+		[
+		Browsable(false)
+		]
+		public PhysicalAxis PhysicalYAxis2Cache
+		{
+			get
+			{
+				return ps_.PhysicalYAxis2Cache;
+			}
+		}
+
+
+		/// <summary>
+		/// A color used to paint the plot background. Mutually exclusive with PlotBackImage and PlotBackBrush
+		/// </summary>
+		/// <remarks>not browsable or bindable because only set method.</remarks>
+		[
+		Category("PlotSurface2D"),
+		Description("Set the plot background color."),
+		Browsable(true),
+		Bindable(false)
+		]
+		public System.Drawing.Color PlotBackColor
+		{
+			set
+			{
+				ps_.PlotBackColor = value;
+			}
+		}
+
+
+		/// <summary>
+		/// An imaged used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+		/// </summary>
+		/// <remarks>not browsable or bindable because only set method.</remarks>
+		[
+		Browsable(false),
+		Bindable(false)
+		]
+		public System.Drawing.Bitmap PlotBackImage
+		{
+			set
+			{
+				ps_.PlotBackImage = value;
+			}
+		}
+
+
+		/// <summary>
+		/// A Rectangle brush used to paint the plot background. Mutually exclusive with PlotBackColor and PlotBackBrush
+		/// </summary>
+		/// <remarks>not browsable or bindable because only set method.</remarks>
+		[
+		Browsable(false),
+		Bindable(false)
+		]
+		public IRectangleBrush PlotBackBrush
+		{
+			set
+			{
+				ps_.PlotBackBrush = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Sets the title to be drawn using a solid brush of this color.
+		/// </summary>
+		/// <remarks>not browsable or bindable because only set method.</remarks>
+		[
+		Browsable(false),
+		Bindable(false)
+		]
+		public Color TitleColor
+		{
+			set
+			{
+				ps_.TitleColor = value;
+			}
+		}
+
+
+		/// <summary>
+		/// The brush used for drawing the title.
+		/// </summary>
+		[
+		Browsable(true),
+		Bindable(true),
+		Description("The brush used for drawing the title."),
+		Category("PlotSurface2D")
+		]
+		public Brush TitleBrush
+		{
+			get
+			{
+				return ps_.TitleBrush;
+			}
+			set
+			{
+				ps_.TitleBrush = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Set smoothing mode for drawing plot objects.
+		/// </summary>
+		[
+		Category("PlotSurface2D"),
+		Description("Set smoothing mode for drawing plot objects."),
+		Browsable(true),
+		Bindable(true)
+		]
+		public System.Drawing.Drawing2D.SmoothingMode SmoothingMode 
+		{ 
+			get
+			{
+				return ps_.SmoothingMode;
+			}
+			set
+			{
+				ps_.SmoothingMode = value;
+			}
+		}
+
+
+        /// <summary>
+		/// Mouse down event handler.
+		/// </summary>
+		/// <param name="e">the event args.</param>
+		protected override void OnMouseDown(MouseEventArgs e)
+		{
+			DoMouseDown(e);
+			base.OnMouseDown(e);
+		}
+
+
+        /// <summary>
+		/// All functionality of the OnMouseDown function is contained here.
+		/// This allows use of the all encompasing PlotSurface.
+		/// </summary>
+		/// <param name="e">The mouse event args from the window we are drawing to.</param>
+		public void DoMouseDown( MouseEventArgs e )
+		{
+			bool dirty = false;
+            foreach (Interactions.Interaction i in interactions_)
+            {
+                i.DoMouseDown(e,this);
+				dirty |= i.DoMouseDown(e,this);
+            }
+			if (dirty)
+			{
+				Refresh();
+			}
+		}
+
+
+        /// <summary>
+        /// Mouse Wheel event handler.
+        /// </summary>
+        /// <param name="e">the event args</param>
+		protected override void OnMouseWheel( System.Windows.Forms.MouseEventArgs e )
+		{
+			DoMouseWheel(e);
+			base.OnMouseWheel(e);
+		}
+
+
+        /// <summary>
+        /// All functionality of the OnMouseWheel function is containd here.
+        /// This allows use of the all encompasing PlotSurface.
+        /// </summary>
+        /// <param name="e">the event args.</param>
+        public void DoMouseWheel(MouseEventArgs e)
+        {
+
+			bool dirty = false;
+            foreach (Interactions.Interaction i in interactions_)
+            {
+                i.DoMouseWheel(e, this);
+				dirty |= i.DoMouseWheel(e, this);
+            }
+			if (dirty)
+			{
+				Refresh();
+			}
+        }
+
+
+        /// <summary>
+		/// All functionality of the OnMouseMove function is contained here.
+		/// This allows use of the all encompasing PlotSurface.
+		/// </summary>
+		/// <param name="e">The mouse event args from the window we are drawing to.</param>
+		/// <param name="ctr">The control that the mouse event happened in.</param>
+		public void DoMouseMove( MouseEventArgs e, System.Windows.Forms.Control ctr )
+		{
+			bool dirty = false;
+            foreach (Interactions.Interaction i in interactions_)
+            {
+                i.DoMouseMove(e, ctr, lastKeyEventArgs_);
+				dirty |= i.DoMouseMove(e, ctr, lastKeyEventArgs_);
+            }
+			if (dirty)
+			{
+				Refresh();
+			}
+
+            // Update coordinates if necessary. 
+
+			if ( coordinates_.Active )
+			{
+				// we are here
+				Point here = new Point( e.X, e.Y );
+				if ( ps_.PlotAreaBoundingBoxCache.Contains(here) )
+				{
+					coordinates_.ShowAlways = true;
+					
+					// according to Måns Erlandson, this can sometimes be the case.
+					if (this.PhysicalXAxis1Cache == null)
+						return;
+					if (this.PhysicalYAxis1Cache == null)
+						return;
+
+					double x = this.PhysicalXAxis1Cache.PhysicalToWorld( here, true );
+					double y = this.PhysicalYAxis1Cache.PhysicalToWorld( here, true );
+					string s = "";
+					if (!DateTimeToolTip)
+					{
+						s = "(" + x.ToString("g4") + "," + y.ToString("g4") + ")"; 
+					}
+					else
+					{
+						DateTime dateTime = new DateTime((long)x);
+						s = dateTime.ToShortDateString() + " " + dateTime.ToLongTimeString() + Environment.NewLine + y.ToString("f4");
+					}
+					coordinates_.SetToolTip( this, s );
+				}
+				else
+				{
+					coordinates_.ShowAlways = false;
+				}
+			}
+
+		}
+
+
+		/// <summary>
+		/// MouseMove event handler.
+		/// </summary>
+		/// <param name="e">The event arguments.</param>
+		protected override void OnMouseMove(MouseEventArgs e)
+		{
+			DoMouseMove( e, this );
+			base.OnMouseMove( e );
+		}
+		
+
+		/// <summary>
+		/// MouseLeave event handler. It has to invalidate the control to get rid of
+		/// any remnant of vertical and horizontal guides.
+		/// </summary>
+		/// <param name="e">The event arguments.</param>
+		protected override void OnMouseLeave(EventArgs e)        
+		{
+
+			DoMouseLeave( e, this );
+			base.OnMouseLeave(e);
+		}
+	
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="e"></param>
+		/// <param name="ctr"></param>
+		public void DoMouseLeave(EventArgs e, System.Windows.Forms.Control ctr) 
+		{
+			bool dirty = false;
+			foreach (Interactions.Interaction i in interactions_)            
+			{
+				dirty = i.DoMouseLeave(e, this) || dirty;
+			}
+			if (dirty)
+				Refresh();
+		}
+
+
+		/// <summary>
+		/// When true, tool tip will display x value as a DateTime. Quick hack - this will probably be 
+		/// changed at some point.
+		/// </summary>
+		[
+		Bindable(true),
+		Browsable(true),
+		Category("PlotSurface2D"),
+		Description("When true, tool tip will display x value as a DateTime. Quick hack - this will probably be changed at some point.")
+		]
+		public bool DateTimeToolTip
+		{
+			get
+			{
+				return dateTimeToolTip_;
+			}
+			set
+			{
+				dateTimeToolTip_ = value;
+			}
+		}
+		private bool dateTimeToolTip_ = false;
+
+
+		/// <summary>
+		/// All functionality of the OnMouseUp function is contained here.
+		/// This allows use of the all encompasing PlotSurface.
+		/// </summary>
+		/// <param name="e">The mouse event args from the window we are drawing to.</param>
+		/// <param name="ctr">The control that the mouse event happened in.</param>
+		public void DoMouseUp( MouseEventArgs e, System.Windows.Forms.Control ctr )
+		{
+			bool dirty = false;
+
+			ArrayList local_interactions = (ArrayList)interactions_.Clone();
+			foreach (Interactions.Interaction i in local_interactions)
+            {
+				dirty |= i.DoMouseUp(e,ctr);
+            }
+			if (dirty)
+			{
+				Refresh();
+			}
+
+            if (e.Button == MouseButtons.Right)
+            {
+                Point here = new Point(e.X, e.Y);
+                selectedObjects_ = ps_.HitTest(here);
+                if (rightMenu_ != null)
+                    rightMenu_.Menu.Show(ctr, here);
+            }
+  
+        }
+
+
+		/// <summary>
+		/// mouse up event handler.
+		/// </summary>
+		/// <param name="e">The event arguments.</param>
+		protected override void OnMouseUp( MouseEventArgs e )
+		{
+			DoMouseUp(e, this);
+			base.OnMouseUp(e);
+		}
+
+
+		/// <summary>
+		/// sets axes to be those saved in the cache.
+		/// </summary>
+		public void OriginalDimensions()
+		{
+			if ( xAxis1ZoomCache_ != null )
+			{
+                this.XAxis1 = xAxis1ZoomCache_;
+                this.XAxis2 = xAxis2ZoomCache_;
+                this.YAxis1 = yAxis1ZoomCache_;
+                this.YAxis2 = yAxis2ZoomCache_;
+
+                xAxis1ZoomCache_ = null;
+                xAxis2ZoomCache_ = null;
+                yAxis1ZoomCache_ = null;
+                yAxis2ZoomCache_ = null;
+            }					
+			this.Refresh();
+		}
+
+        private void DrawHorizontalSelection(Point start, Point end, System.Windows.Forms.UserControl ctr)
+        {
+            // the clipping rectangle in screen coordinates
+            Rectangle clip = ctr.RectangleToScreen(
+                new Rectangle(
+                (int)ps_.PlotAreaBoundingBoxCache.X,
+                (int)ps_.PlotAreaBoundingBoxCache.Y,
+                (int)ps_.PlotAreaBoundingBoxCache.Width,
+                (int)ps_.PlotAreaBoundingBoxCache.Height));
+
+            start = ctr.PointToScreen(start);
+            end = ctr.PointToScreen(end);
+
+            ControlPaint.FillReversibleRectangle(
+                new Rectangle((int)Math.Min(start.X,end.X), (int)clip.Y, (int)Math.Abs(end.X-start.X), (int)clip.Height),
+                Color.White );
+
+        }
+
+
+		/// <summary>
+		/// Add an axis constraint to the plot surface. Axis constraints can
+		/// specify relative world-pixel scalings, absolute axis positions etc.
+		/// </summary>
+		/// <param name="c">The axis constraint to add.</param>
+		public void AddAxesConstraint( AxesConstraint c )
+		{
+			ps_.AddAxesConstraint( c );
+		}
+
+
+		/// <summary>
+		/// Print the chart as currently shown by the control
+		/// </summary>
+		/// <param name="preview">If true, show print preview window.</param>
+		public void Print( bool preview ) 
+		{
+			PrintDocument printDocument = new PrintDocument();
+			printDocument.PrintPage += new PrintPageEventHandler(NPlot_PrintPage);
+			printDocument.DefaultPageSettings.Landscape = true;
+				 	
+			DialogResult result;
+			if (!preview) 
+			{
+				PrintDialog dlg = new PrintDialog();
+				dlg.Document = printDocument;
+				result = dlg.ShowDialog();
+			} 
+			else 
+			{
+				PrintPreviewDialog dlg = new PrintPreviewDialog();
+				dlg.Document = printDocument;
+				result = dlg.ShowDialog();
+			}
+			if (result == DialogResult.OK) 
+			{
+				try 
+				{
+					printDocument.Print();
+				}								 				
+				catch 
+				{
+					Console.WriteLine( "caught\n" );
+				}
+			}
+		}
+
+
+		private void NPlot_PrintPage(object sender, PrintPageEventArgs ev) 
+		{
+			Rectangle r = ev.MarginBounds;
+			this.Draw( ev.Graphics, r );
+			ev.HasMorePages = false;
+		}
+	
+		
+		/// <summary>
+		/// Coppies the chart currently shown in the control to the clipboard as an image.
+		/// </summary>
+		public void CopyToClipboard()
+		{
+			System.Drawing.Bitmap b = new System.Drawing.Bitmap( this.Width, this.Height );
+			System.Drawing.Graphics g = Graphics.FromImage( b );
+			g.Clear(Color.White);
+			this.Draw( g, new Rectangle( 0, 0, b.Width-1, b.Height-1 ) );
+			Clipboard.SetDataObject( b, true );
+		}
+
+
+		/// <summary>
+		/// Coppies data in the current plot surface view window to the clipboard
+		/// as text.
+		/// </summary>
+		public void CopyDataToClipboard()
+		{
+
+			System.Text.StringBuilder sb = new System.Text.StringBuilder();
+
+			for (int i=0; i<ps_.Drawables.Count; ++i)
+			{
+				IPlot plot = ps_.Drawables[i] as IPlot;
+				if (plot != null)
+				{
+					Axis xAxis = ps_.WhichXAxis( plot );
+					Axis yAxis = ps_.WhichYAxis( plot );
+
+					RectangleD region = new RectangleD( 
+						xAxis.WorldMin, 
+						yAxis.WorldMin,
+						xAxis.WorldMax - xAxis.WorldMin,
+						yAxis.WorldMax - yAxis.WorldMin );
+
+					plot.WriteData( sb, region, true );
+				}
+			}
+
+			Clipboard.SetDataObject( sb.ToString(), true );
+
+		}
+
+
+        /// <summary>
+        /// Remove a drawable object from the plot surface.
+        /// </summary>
+        /// <param name="p">the drawable to remove</param>
+        /// <param name="updateAxes">whether or not to update the axes after removing the idrawable.</param>
+        public void Remove(IDrawable p, bool updateAxes)
+        {
+            ps_.Remove(p, updateAxes);
+        }
+
+
+        /// <summary>
+		/// Gets an array list containing all drawables currently added to the PlotSurface2D.
+		/// </summary>
+		[
+		Browsable(false),
+		Bindable(false)
+		]
+		public ArrayList Drawables
+		{
+			get
+			{
+				return ps_.Drawables;
+			}
+		}
+
+
+		/// <summary>
+		/// Sets the right context menu. Custom menus can be designed by overriding
+		/// NPlot.Windows.PlotSurface2D.ContextMenu.
+		/// </summary>
+		[
+		Browsable(false),
+		Bindable(false)
+		]
+		public NPlot.Windows.PlotSurface2D.PlotContextMenu RightMenu
+		{
+			get
+			{
+				return rightMenu_;
+			}
+			set
+			{
+				rightMenu_ = value;
+				if (rightMenu_ != null)
+				{
+					rightMenu_.PlotSurface2D = this;
+				}
+			}
+		}
+		private NPlot.Windows.PlotSurface2D.PlotContextMenu rightMenu_ = null;
+
+
+		/// <summary>
+		/// Gets an instance of a NPlot.Windows.PlotSurface2D.ContextMenu that
+		/// is useful in typical situations.
+		/// </summary>
+		public static PlotContextMenu DefaultContextMenu
+		{
+			get
+			{
+				return new NPlot.Windows.PlotSurface2D.PlotContextMenu();
+			}
+		}
+
+
+        /// <summary>
+        /// Allows access to the PlotSurface2D.
+        /// </summary>
+        [
+		Browsable(false),
+		Bindable(false)
+		]
+        public NPlot.PlotSurface2D Inner
+        {
+            get
+            {
+                return ps_;
+            }
+        }
+
+
+        /// <summary>
+        /// Remembers the current axes - useful in interactions.
+        /// </summary>
+        public void CacheAxes()
+        {
+            if (xAxis1ZoomCache_ == null && xAxis2ZoomCache_ == null &&
+                 yAxis1ZoomCache_ == null && yAxis2ZoomCache_ == null)
+            {
+                if (this.XAxis1 != null)
+                {
+                    xAxis1ZoomCache_ = (Axis)this.XAxis1.Clone();
+                }
+                if (this.XAxis2 != null)
+                {
+                    xAxis2ZoomCache_ = (Axis)this.XAxis2.Clone();
+                }
+                if (this.YAxis1 != null)
+                {
+                    yAxis1ZoomCache_ = (Axis)this.YAxis1.Clone();
+                }
+                if (this.YAxis2 != null)
+                {
+                    yAxis2ZoomCache_ = (Axis)this.YAxis2.Clone();
+                }
+            }
+        }
+
+
+        /// <summary>
+        /// Encapsulates a number of separate "Interactions". An interaction is basically 
+        /// a set of handlers for mouse and keyboard events that work together in a 
+        /// specific way. 
+        /// </summary>
+        public abstract class Interactions
+        {
+
+            /// <summary>
+            /// Base class for an interaction. All methods are virtual. Not abstract as not all interactions
+            /// need to use all methods. Default functionality for each method is to do nothing. 
+            /// </summary>
+            public class Interaction
+            {
+                /// <summary>
+                /// Handler for this interaction if a mouse down event is received.
+                /// </summary>
+                /// <param name="e">event args</param>
+                /// <param name="ctr">reference to the control</param>
+                /// <returns>true if plot surface needs refreshing.</returns>
+                public virtual bool DoMouseDown(MouseEventArgs e, System.Windows.Forms.Control ctr) { return false; }
+                
+                /// <summary>
+                /// Handler for this interaction if a mouse up event is received.
+                /// </summary>
+				/// <param name="e">event args</param>
+				/// <param name="ctr">reference to the control</param>
+				/// <returns>true if plot surface needs refreshing.</returns>
+                public virtual bool DoMouseUp(MouseEventArgs e, System.Windows.Forms.Control ctr) { return false; }
+                
+                /// <summary>
+                /// Handler for this interaction if a mouse move event is received.
+                /// </summary>
+				/// <param name="e">event args</param>
+				/// <param name="ctr">reference to the control</param>
+                /// <param name="lastKeyEventArgs"></param>
+                /// <returns>true if plot surface needs refreshing.</returns>
+                public virtual bool DoMouseMove(MouseEventArgs e, System.Windows.Forms.Control ctr, KeyEventArgs lastKeyEventArgs) { return false; }
+                
+                /// <summary>
+                /// Handler for this interaction if a mouse move event is received.
+                /// </summary>
+				/// <param name="e">event args</param>
+				/// <param name="ctr">reference to the control</param>
+				/// <returns>true if plot surface needs refreshing.</returns>
+                public virtual bool DoMouseWheel(MouseEventArgs e, System.Windows.Forms.Control ctr) { return false; }
+                
+				/// <summary>
+				/// Handler for this interaction if a mouse Leave event is received.
+				/// </summary>
+				/// <param name="e">event args</param>
+				/// <param name="ctr">reference to the control</param>
+				/// <returns>true if the plot surface needs refreshing.</returns>
+				public virtual bool DoMouseLeave(EventArgs e, System.Windows.Forms.Control ctr) { return false; }
+
+                /// <summary>
+                /// Handler for this interaction if a paint event is received.
+                /// </summary>
+                /// <param name="pe">paint event args</param>
+                /// <param name="width"></param>
+                /// <param name="height"></param>
+                public virtual void DoPaint(PaintEventArgs pe, int width, int height) { }
+            }
+
+
+            #region RubberBandSelection
+            /// <summary>
+            /// 
+            /// </summary>
+            public class RubberBandSelection : Interaction
+            {
+                private bool selectionInitiated_ = false;
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseDown(MouseEventArgs e, Control ctr)
+                {
+                    // keep track of the start point and flag that select initiated.
+                    selectionInitiated_ = true;
+                    startPoint_.X = e.X;
+                    startPoint_.Y = e.Y;
+
+                    // invalidate the end point
+                    endPoint_ = unset_;
+
+					return false;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                /// <param name="lastKeyEventArgs"></param>
+                public override bool DoMouseMove(MouseEventArgs e, Control ctr, KeyEventArgs lastKeyEventArgs)
+                {
+
+                    if ((e.Button == MouseButtons.Left) && selectionInitiated_)
+                    {
+                        // we are here
+                        Point here = new Point(e.X, e.Y);
+
+                        // delete the previous box
+                        if (endPoint_ != unset_)
+                        {
+                            this.DrawRubberBand(startPoint_, endPoint_, ctr);
+                        }
+                        endPoint_ = here;
+
+                        // and redraw the last one
+                        this.DrawRubberBand(startPoint_, endPoint_, ctr);
+
+                    }
+                   
+					return false;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseUp(MouseEventArgs e, Control ctr)
+                {
+
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    // handle left button (selecting region).
+                    if ((e.Button == MouseButtons.Left) && selectionInitiated_)
+                    {
+                        endPoint_.X = e.X;
+                        endPoint_.Y = e.Y;
+
+                        // flag stopped selecting.
+                        selectionInitiated_ = false;
+
+                        if (endPoint_ != unset_)
+                        {
+                            this.DrawRubberBand(startPoint_, endPoint_, ctr);
+                        }
+
+                        Point minPoint = new Point(0, 0);
+                        minPoint.X = Math.Min(startPoint_.X, endPoint_.X);
+                        minPoint.Y = Math.Min(startPoint_.Y, endPoint_.Y);
+
+                        Point maxPoint = new Point(0, 0);
+                        maxPoint.X = Math.Max(startPoint_.X, endPoint_.X);
+                        maxPoint.Y = Math.Max(startPoint_.Y, endPoint_.Y);
+
+                        Rectangle r = ps.PlotAreaBoundingBoxCache;
+                        if (minPoint != maxPoint && (r.Contains(minPoint) || r.Contains(maxPoint)))
+                        {
+                            ((Windows.PlotSurface2D)ctr).CacheAxes();
+
+                            ((Windows.PlotSurface2D)ctr).PhysicalXAxis1Cache.SetWorldLimitsFromPhysical(minPoint, maxPoint);
+                            ((Windows.PlotSurface2D)ctr).PhysicalXAxis2Cache.SetWorldLimitsFromPhysical(minPoint, maxPoint);
+                            ((Windows.PlotSurface2D)ctr).PhysicalYAxis1Cache.SetWorldLimitsFromPhysical(maxPoint, minPoint);
+                            ((Windows.PlotSurface2D)ctr).PhysicalYAxis2Cache.SetWorldLimitsFromPhysical(maxPoint, minPoint);
+
+                            // reset the start/end points
+                            startPoint_ = unset_;
+                            endPoint_ = unset_;
+
+                            ((Windows.PlotSurface2D)ctr).InteractionOccured(this);
+
+							return true;
+                        }
+					}
+
+					return false;
+                }
+
+                /// <summary>
+                /// Draws a rectangle representing selection area. 
+                /// </summary>
+                /// <param name="start">a corner of the rectangle.</param>
+                /// <param name="end">a corner of the rectangle diagonally opposite the first.</param>
+                /// <param name="ctr">The control to draw to - this may not be us, if we have
+                /// been contained by a PlotSurface.</param>
+                private void DrawRubberBand(Point start, Point end, System.Windows.Forms.Control ctr)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    Rectangle rect = new Rectangle();
+
+                    // the clipping rectangle in screen coordinates
+                    Rectangle clip = ctr.RectangleToScreen(
+                        new Rectangle(
+                        (int)ps.PlotAreaBoundingBoxCache.X,
+                        (int)ps.PlotAreaBoundingBoxCache.Y,
+                        (int)ps.PlotAreaBoundingBoxCache.Width,
+                        (int)ps.PlotAreaBoundingBoxCache.Height));
+
+                    // convert to screen coords
+                    start = ctr.PointToScreen(start);
+                    end = ctr.PointToScreen(end);
+
+                    // now, "normalize" the rectangle
+                    if (start.X < end.X)
+                    {
+                        rect.X = start.X;
+                        rect.Width = end.X - start.X;
+                    }
+                    else
+                    {
+                        rect.X = end.X;
+                        rect.Width = start.X - end.X;
+                    }
+                    if (start.Y < end.Y)
+                    {
+                        rect.Y = start.Y;
+                        rect.Height = end.Y - start.Y;
+                    }
+                    else
+                    {
+                        rect.Y = end.Y;
+                        rect.Height = start.Y - end.Y;
+                    }
+                    rect = Rectangle.Intersect(rect, clip);
+
+                    ControlPaint.DrawReversibleFrame(
+                        new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height),
+                        Color.White, FrameStyle.Dashed);
+
+                }
+
+                private Point startPoint_ = new Point(-1, -1);
+                private Point endPoint_ = new Point(-1, -1);
+                // this is the condition for an unset point
+                private Point unset_ = new Point(-1, -1);
+
+            }
+            #endregion
+            #region HorizontalGuideline
+            /// <summary>
+            /// Horizontal line interaction
+            /// </summary>
+            public class HorizontalGuideline : Interaction
+            {
+                private int barPos_;
+                private Color color_;
+
+                /// <summary>
+                /// Constructor
+                /// </summary>
+                public HorizontalGuideline()
+                {
+                    color_ = Color.Black;
+                }
+
+                /// <summary>
+                /// Constructor
+                /// </summary>
+                /// <param name="lineColor"></param>
+                public HorizontalGuideline(Color lineColor)
+                {
+                    color_ = lineColor;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="pe"></param>
+                /// <param name="width"></param>
+                /// <param name="height"></param>
+                public override void DoPaint(PaintEventArgs pe, int width, int height)
+                {
+                    barPos_ = -1;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                /// <param name="lastKeyEventArgs"></param>
+				public override bool DoMouseMove(MouseEventArgs e, System.Windows.Forms.Control ctr, KeyEventArgs lastKeyEventArgs)
+				{
+
+					NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+					// if mouse isn't in plot region, then don't draw horizontal line
+					if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < ps.PlotAreaBoundingBoxCache.Right &&
+						e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < (ps.PlotAreaBoundingBoxCache.Bottom-1))
+					{
+
+						if (ps.PhysicalXAxis1Cache != null)
+						{
+
+							// the clipping rectangle in screen coordinates
+							Rectangle clip = ctr.RectangleToScreen(
+								new Rectangle(
+								(int)ps.PlotAreaBoundingBoxCache.X,
+								(int)ps.PlotAreaBoundingBoxCache.Y,
+								(int)ps.PlotAreaBoundingBoxCache.Width,
+								(int)ps.PlotAreaBoundingBoxCache.Height));
+
+							Point p = ctr.PointToScreen(new Point(e.X, e.Y));
+
+							if (barPos_ != -1)
+							{
+								ControlPaint.DrawReversibleLine(
+									new Point(clip.Left, barPos_),
+									new Point(clip.Right, barPos_), color_);
+							}
+
+							if (p.Y < clip.Bottom && p.Y > clip.Top)
+							{
+								ControlPaint.DrawReversibleLine(
+									new Point(clip.Left, p.Y),
+									new Point(clip.Right, p.Y), color_);
+
+								barPos_ = p.Y;
+							}
+							else
+							{
+								barPos_ = -1;
+							}
+
+						}
+
+					}
+					else
+					{
+
+						if (barPos_ != -1)
+						{
+							Rectangle clip = ctr.RectangleToScreen(
+								new Rectangle(
+								(int)ps.PlotAreaBoundingBoxCache.X,
+								(int)ps.PlotAreaBoundingBoxCache.Y,
+								(int)ps.PlotAreaBoundingBoxCache.Width,
+								(int)ps.PlotAreaBoundingBoxCache.Height) );
+
+							ControlPaint.DrawReversibleLine(
+								new Point(clip.Left, barPos_),
+								new Point(clip.Right, barPos_), color_);
+							barPos_ = -1;
+						}
+
+					}
+
+					return false;
+				}
+
+
+				/// <summary>
+				/// 
+				/// </summary>
+				/// <param name="e"></param>
+				/// <param name="ctr"></param>
+				/// <returns></returns>
+				public override bool DoMouseLeave(EventArgs e, System.Windows.Forms.Control ctr)
+				{
+					if (barPos_ != -1)             
+					{
+						NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+				
+						Rectangle clip = ctr.RectangleToScreen(
+							new Rectangle(
+							(int)ps.PlotAreaBoundingBoxCache.X,
+							(int)ps.PlotAreaBoundingBoxCache.Y,
+							(int)ps.PlotAreaBoundingBoxCache.Width,
+							(int)ps.PlotAreaBoundingBoxCache.Height));
+			
+						ControlPaint.DrawReversibleLine(
+							new Point(clip.Left, barPos_),
+							new Point(clip.Right, barPos_), color_);
+
+						barPos_ = -1;
+					}
+					return false;
+				}
+
+            }
+            #endregion
+            #region VerticalGuideline
+            /// <summary>
+            /// 
+            /// </summary>
+            public class VerticalGuideline : Interaction
+            {
+                private int barPos_;
+                private Color color_;
+
+                /// <summary>
+                /// 
+                /// </summary>
+                public VerticalGuideline()
+                {
+                    color_ = Color.Black;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="lineColor"></param>
+                public VerticalGuideline(Color lineColor)
+                {
+                    color_ = lineColor;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="pe"></param>
+                /// <param name="width"></param>
+                /// <param name="height"></param>
+                public override void DoPaint(PaintEventArgs pe, int width, int height)
+                {
+                    barPos_ = -1;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                /// <param name="lastKeyEventArgs"></param>
+                public override bool DoMouseMove(MouseEventArgs e, System.Windows.Forms.Control ctr, KeyEventArgs lastKeyEventArgs)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    // if mouse isn't in plot region, then don't draw horizontal line
+                    if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < (ps.PlotAreaBoundingBoxCache.Right-1) &&
+                        e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < ps.PlotAreaBoundingBoxCache.Bottom)
+                    {
+                        if (ps.PhysicalXAxis1Cache != null)
+                        {
+
+                            // the clipping rectangle in screen coordinates
+                            Rectangle clip = ctr.RectangleToScreen(
+                                new Rectangle(
+                                (int)ps.PlotAreaBoundingBoxCache.X,
+                                (int)ps.PlotAreaBoundingBoxCache.Y,
+                                (int)ps.PlotAreaBoundingBoxCache.Width,
+                                (int)ps.PlotAreaBoundingBoxCache.Height));
+
+                            Point p = ctr.PointToScreen(new Point(e.X, e.Y));
+
+                            if (barPos_ != -1)
+                            {
+                                ControlPaint.DrawReversibleLine(
+                                    new Point(barPos_, clip.Top),
+                                    new Point(barPos_, clip.Bottom), color_);
+                            }
+
+                            if (p.X < clip.Right && p.X > clip.Left)
+                            {
+                                ControlPaint.DrawReversibleLine(
+                                    new Point(p.X, clip.Top),
+                                    new Point(p.X, clip.Bottom), color_);
+                                barPos_ = p.X;
+                            }
+                            else
+                            {
+                                barPos_ = -1;
+                            }
+
+                        }
+
+                    }
+                    else
+                    {
+                        
+  
+                        if (barPos_ != -1)
+                        {
+
+						    Rectangle clip = ctr.RectangleToScreen(
+								new Rectangle(
+									(int)ps.PlotAreaBoundingBoxCache.X,
+									(int)ps.PlotAreaBoundingBoxCache.Y,
+									(int)ps.PlotAreaBoundingBoxCache.Width,
+									(int)ps.PlotAreaBoundingBoxCache.Height)
+								);
+
+                            ControlPaint.DrawReversibleLine(
+                                new Point(barPos_, clip.Top),
+                                new Point(barPos_, clip.Bottom), color_);
+
+							barPos_ = -1;
+                        }
+
+                    }
+
+					return false;
+                }
+
+				/// <summary>
+				/// Handler for mouse leave event
+				/// </summary>
+				/// <param name="e">event args</param>
+				/// <param name="ctr"></param>
+				/// <returns></returns>
+				public override bool DoMouseLeave(EventArgs e, System.Windows.Forms.Control ctr)	                 
+				{
+					if (barPos_ != -1)       
+					{
+						NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+				
+						Rectangle clip = ctr.RectangleToScreen(
+							new Rectangle(
+							(int)ps.PlotAreaBoundingBoxCache.X,
+							(int)ps.PlotAreaBoundingBoxCache.Y,
+							(int)ps.PlotAreaBoundingBoxCache.Width,
+							(int)ps.PlotAreaBoundingBoxCache.Height));
+				
+						ControlPaint.DrawReversibleLine(
+							new Point(barPos_, clip.Top),
+							new Point(barPos_, clip.Bottom), color_);
+						barPos_ = -1;
+					}
+					return false;
+				}
+
+            }
+            #endregion
+            #region HorizontalDrag
+            /// <summary>
+            /// 
+            /// </summary>
+            public class HorizontalDrag : Interaction
+            {
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseDown(MouseEventArgs e, Control ctr)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < (ps.PlotAreaBoundingBoxCache.Right) &&
+                        e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < ps.PlotAreaBoundingBoxCache.Bottom)
+                    {
+                        dragInitiated_ = true;
+
+                        lastPoint_.X = e.X;
+                        lastPoint_.Y = e.Y;
+                    }
+
+					return false;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                /// <param name="lastKeyEventArgs"></param>
+                public override bool DoMouseMove(MouseEventArgs e, Control ctr, KeyEventArgs lastKeyEventArgs)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+					if ((e.Button == MouseButtons.Left) && dragInitiated_)
+					{
+						int diffX = e.X - lastPoint_.X;
+
+						((Windows.PlotSurface2D)ctr).CacheAxes();
+
+						// original code was using PixelWorldLength of the physical axis
+						// but it was not working for non-linear axes - the code below works
+						// in all cases
+						if (ps.XAxis1 != null)
+						{
+							Axis axis = ps.XAxis1;
+							PointF pMin = ps.PhysicalXAxis1Cache.PhysicalMin;
+							PointF pMax = ps.PhysicalXAxis1Cache.PhysicalMax;
+
+
+                            PointF physicalWorldMin = pMin;
+                            PointF physicalWorldMax = pMax;
+							physicalWorldMin.X -= diffX;
+							physicalWorldMax.X -= diffX;
+							double newWorldMin = axis.PhysicalToWorld(physicalWorldMin, pMin, pMax, false);
+							double newWorldMax = axis.PhysicalToWorld(physicalWorldMax, pMin, pMax, false);
+							axis.WorldMin = newWorldMin;
+							axis.WorldMax = newWorldMax;
+						}
+						if (ps.XAxis2 != null)
+						{
+							Axis axis = ps.XAxis2;
+							PointF pMin = ps.PhysicalXAxis2Cache.PhysicalMin;
+							PointF pMax = ps.PhysicalXAxis2Cache.PhysicalMax;
+						
+                            PointF physicalWorldMin = pMin;
+                            PointF physicalWorldMax = pMax;
+							physicalWorldMin.X -= diffX;
+							physicalWorldMax.X -= diffX;
+							double newWorldMin = axis.PhysicalToWorld(physicalWorldMin, pMin, pMax, false);
+							double newWorldMax = axis.PhysicalToWorld(physicalWorldMax, pMin, pMax, false);
+							axis.WorldMin = newWorldMin;
+							axis.WorldMax = newWorldMax;
+						}
+						
+						lastPoint_ = new Point(e.X, e.Y);
+
+						((Windows.PlotSurface2D)ctr).InteractionOccured(this);
+
+						return true;
+					}
+                    
+					return false;
+                }
+
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseUp(MouseEventArgs e, Control ctr)
+                {
+                    if ((e.Button == MouseButtons.Left) && dragInitiated_)
+                    {
+                       lastPoint_ = unset_;
+                       dragInitiated_ = false;
+                    }
+					return false;
+                }
+
+                private bool dragInitiated_ = false;
+                private Point lastPoint_ = new Point(-1, -1);
+                // this is the condition for an unset point
+                private Point unset_ = new Point(-1, -1);
+            }
+            #endregion
+            #region VerticalDrag
+            /// <summary>
+            /// 
+            /// </summary>
+            public class VerticalDrag : Interaction
+            {
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseDown(MouseEventArgs e, Control ctr)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < (ps.PlotAreaBoundingBoxCache.Right) &&
+                        e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < ps.PlotAreaBoundingBoxCache.Bottom)
+                    {
+                        dragInitiated_ = true;
+
+                        lastPoint_.X = e.X;
+                        lastPoint_.Y = e.Y;
+                    }
+
+					return false;
+                }
+
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                /// <param name="lastKeyEventArgs"></param>
+				public override bool DoMouseMove(MouseEventArgs e, Control ctr, KeyEventArgs lastKeyEventArgs)
+				{
+					NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+					if ((e.Button == MouseButtons.Left) && dragInitiated_)
+					{
+  
+						int diffY = e.Y - lastPoint_.Y;
+
+						((Windows.PlotSurface2D)ctr).CacheAxes();
+
+						if (ps.YAxis1 != null)
+						{
+							Axis axis = ps.YAxis1;
+							PointF pMin = ps.PhysicalYAxis1Cache.PhysicalMin;
+							PointF pMax = ps.PhysicalYAxis1Cache.PhysicalMax;
+								
+							PointF physicalWorldMin = pMin;
+							PointF physicalWorldMax = pMax;
+							physicalWorldMin.Y -= diffY;
+							physicalWorldMax.Y -= diffY;
+							double newWorldMin = axis.PhysicalToWorld(physicalWorldMin, pMin, pMax, false);
+							double newWorldMax = axis.PhysicalToWorld(physicalWorldMax, pMin, pMax, false);
+							axis.WorldMin = newWorldMin;
+							axis.WorldMax = newWorldMax;
+						}
+						if (ps.YAxis2 != null)
+						{
+							Axis axis = ps.YAxis2;
+							PointF pMin = ps.PhysicalYAxis2Cache.PhysicalMin;
+							PointF pMax = ps.PhysicalYAxis2Cache.PhysicalMax;
+
+							PointF physicalWorldMin = pMin;
+							PointF physicalWorldMax = pMax;
+							physicalWorldMin.Y -= diffY;
+							physicalWorldMax.Y -= diffY;
+							double newWorldMin = axis.PhysicalToWorld(physicalWorldMin, pMin, pMax, false);
+							double newWorldMax = axis.PhysicalToWorld(physicalWorldMax, pMin, pMax, false);
+							axis.WorldMin = newWorldMin;
+							axis.WorldMax = newWorldMax;
+						}
+
+						lastPoint_ = new Point(e.X, e.Y);
+
+						((Windows.PlotSurface2D)ctr).InteractionOccured(this);
+
+						return true;
+					}
+				
+					return false;
+				}
+
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseUp(MouseEventArgs e, Control ctr)
+                {
+                    if ((e.Button == MouseButtons.Left) && dragInitiated_)
+                    {
+                        lastPoint_ = unset_;
+                        dragInitiated_ = false;
+                    }
+
+					return false;
+                }
+
+                private bool dragInitiated_ = false;
+                private Point lastPoint_ = new Point(-1, -1);
+                // this is the condition for an unset point
+                private Point unset_ = new Point(-1, -1);
+            }
+            #endregion
+            #region HorizontalRangeSelection
+            /// <summary>
+            /// This plot intraction allows the user to select horizontal regions.
+            /// </summary>
+            public class HorizontalRangeSelection : Interaction
+            {
+                private bool selectionInitiated_ = false;
+                private Point startPoint_ = new Point(-1, -1);
+                private Point endPoint_ = new Point(-1, -1);
+                private Point previousPoint_ = new Point(-1, -1);
+                private Point unset_ = new Point(-1, -1);
+				private int minimumPixelDistanceForSelect_ = 5; 
+				private double smallestAllowedRange_ = double.Epsilon * 100.0;
+
+
+				/// <summary>
+				/// Default constructor
+				/// </summary>
+				public HorizontalRangeSelection()
+				{
+				}
+
+
+				/// <summary>
+				/// Constructor
+				/// </summary>
+				/// <param name="smallestAllowedRange">the smallest distance between the selected xmin and xmax for the selection to be performed.</param>
+				public HorizontalRangeSelection( double smallestAllowedRange )
+				{
+					this.smallestAllowedRange_ = smallestAllowedRange;
+				}
+
+
+				/// <summary>
+				/// The minimum width of the selected region (in pixels) for the interaction to zoom.
+				/// </summary>
+				public int MinimumPixelDistanceForSelect
+				{
+					get
+					{
+						return minimumPixelDistanceForSelect_;
+					}
+					set
+					{
+						minimumPixelDistanceForSelect_ = value;
+					}
+				}
+
+
+				/// <summary>
+				/// The smallest range (distance between world min and world max) selectable.
+				/// If a smaller region is selected, the selection will do nothing.
+				/// </summary>
+				public double SmallestAllowedRange
+				{
+					get
+					{
+						return smallestAllowedRange_;
+					}
+					set
+					{
+						smallestAllowedRange_ = value;
+					}
+				}
+
+
+				/// <summary>
+				/// Handler for mouse down event for this interaction
+				/// </summary>
+				/// <param name="e">the mouse event args</param>
+				/// <param name="ctr">the plot surface this event applies to</param>
+                public override bool DoMouseDown(MouseEventArgs e, Control ctr )
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < ps.PlotAreaBoundingBoxCache.Right &&
+                        e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < ps.PlotAreaBoundingBoxCache.Bottom)
+                    {
+
+                        // keep track of the start point and flag that select initiated.
+                        selectionInitiated_ = true;
+                        startPoint_.X = e.X;
+                        startPoint_.Y = e.Y;
+
+                        previousPoint_.X = e.X;
+                        previousPoint_.Y = e.Y;
+
+                        // invalidate the end point
+                        endPoint_ = unset_;
+
+                        return false;
+                    }
+
+                    selectionInitiated_ = false;
+                    endPoint_ = unset_;
+                    startPoint_ = unset_;
+					return false;
+                }
+
+                
+				/// <summary>
+				/// Handler for mouse move event for this interaction
+				/// </summary>
+				/// <param name="e">the mouse event args</param>
+				/// <param name="ctr">the plot surface this event applies to</param>
+                /// <param name="lastKeyEventArgs"></param>
+                public override bool DoMouseMove(MouseEventArgs e, Control ctr, KeyEventArgs lastKeyEventArgs)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    // if dragging on axis to zoom.
+                    if ((e.Button == MouseButtons.Left) && selectionInitiated_)
+                    {
+                        Point endPoint_ = previousPoint_;
+                        if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < ps.PlotAreaBoundingBoxCache.Right &&
+                            e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < ps.PlotAreaBoundingBoxCache.Bottom)
+                        {
+                            endPoint_ = new Point(e.X, e.Y);
+                            this.DrawHorizontalSelection(previousPoint_, endPoint_, ctr);
+                            previousPoint_ = endPoint_;
+                        }
+                        else
+                        {
+                            endPoint_ = new Point(e.X, e.Y);
+                            if (e.X < ps.PlotAreaBoundingBoxCache.Left) endPoint_.X = ps.PlotAreaBoundingBoxCache.Left + 1;
+                            if (e.X > ps.PlotAreaBoundingBoxCache.Right) endPoint_.X = ps.PlotAreaBoundingBoxCache.Right - 1;
+                            this.DrawHorizontalSelection(previousPoint_, endPoint_, ctr);
+                            previousPoint_ = endPoint_;
+                        }
+                    }
+
+					return false;
+                }
+
+                
+                /// <summary>
+                /// Handler for mouse up event for this interaction
+                /// </summary>
+                /// <param name="e">the mouse event args</param>
+                /// <param name="ctr">the plot surface this event applies to</param>
+                public override bool DoMouseUp(MouseEventArgs e, Control ctr)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    if ((e.Button == MouseButtons.Left) && selectionInitiated_)
+                    {
+                        endPoint_.X = e.X;
+                        endPoint_.Y = e.Y;
+                        if (e.X < ps.PlotAreaBoundingBoxCache.Left) endPoint_.X = ps.PlotAreaBoundingBoxCache.Left + 1;
+                        if (e.X > ps.PlotAreaBoundingBoxCache.Right) endPoint_.X = ps.PlotAreaBoundingBoxCache.Right - 1;
+
+                        // flag stopped selecting.
+                        selectionInitiated_ = false;
+
+                        if (endPoint_ != unset_)
+                        {
+                            this.DrawHorizontalSelection(startPoint_, endPoint_, ctr);
+                        }
+
+						// ignore very small selections
+						if (Math.Abs(endPoint_.X - startPoint_.X) < minimumPixelDistanceForSelect_)  
+						{
+							return false;
+						}
+
+                        ((Windows.PlotSurface2D)ctr).CacheAxes();
+
+						// determine the new x axis 1 world limits (and check to see if they are far enough appart).
+						double xAxis1Min = double.NaN;
+						double xAxis1Max = double.NaN;
+                        if (ps.XAxis1 != null)
+                        {
+                            int x1 = (int)Math.Min(endPoint_.X, startPoint_.X);
+                            int x2 = (int)Math.Max(endPoint_.X, startPoint_.X);
+                            int y = ps.PhysicalXAxis1Cache.PhysicalMax.Y;
+
+							xAxis1Min = ps.PhysicalXAxis1Cache.PhysicalToWorld(new Point(x1, y), true);
+							xAxis1Max = ps.PhysicalXAxis1Cache.PhysicalToWorld(new Point(x2, y), true);
+							if (xAxis1Max - xAxis1Min < this.smallestAllowedRange_)
+							{
+								return false;
+							}
+                        }
+
+						// determine the new x axis 2 world limits (and check to see if they are far enough appart).
+                        double xAxis2Min = double.NaN;
+						double xAxis2Max = double.NaN;
+						if (ps.XAxis2 != null)
+                        {
+                            int x1 = (int)Math.Min(endPoint_.X, startPoint_.X);
+                            int x2 = (int)Math.Max(endPoint_.X, startPoint_.X);
+                            int y = ps.PhysicalXAxis2Cache.PhysicalMax.Y;
+
+                            xAxis2Min = ps.PhysicalXAxis2Cache.PhysicalToWorld(new Point(x1, y), true);
+                            xAxis2Max = ps.PhysicalXAxis2Cache.PhysicalToWorld(new Point(x2, y), true);
+							if (xAxis2Max - xAxis2Min < smallestAllowedRange_)
+							{
+								return false;
+							}
+                        }
+
+						// now actually update the world limits.
+
+						if (ps.XAxis1 != null)
+						{
+							ps.XAxis1.WorldMax = xAxis1Max;
+							ps.XAxis1.WorldMin = xAxis1Min;
+						}
+
+						if (ps.XAxis2 != null)
+						{
+							ps.XAxis2.WorldMax = xAxis2Max;
+							ps.XAxis2.WorldMin = xAxis2Min;
+						}
+
+                        ((Windows.PlotSurface2D)ctr).InteractionOccured(this);
+
+                        return true;
+                    }
+
+					return false;
+                }
+
+
+                private void DrawHorizontalSelection(Point start, Point end, System.Windows.Forms.Control ctr)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    // the clipping rectangle in screen coordinates
+                    Rectangle clip = ctr.RectangleToScreen(
+                        new Rectangle(
+                        (int)ps.PlotAreaBoundingBoxCache.X,
+                        (int)ps.PlotAreaBoundingBoxCache.Y,
+                        (int)ps.PlotAreaBoundingBoxCache.Width,
+                        (int)ps.PlotAreaBoundingBoxCache.Height));
+
+                    start = ctr.PointToScreen(start);
+                    end = ctr.PointToScreen(end);
+
+                    ControlPaint.FillReversibleRectangle(
+                        new Rectangle((int)Math.Min(start.X, end.X), (int)clip.Y, (int)Math.Abs(end.X - start.X), (int)clip.Height),
+                        Color.White);
+
+                }
+
+            }
+            #endregion
+            #region AxisDrag
+            /// <summary>
+            /// 
+            /// </summary>
+            public class AxisDrag : Interaction
+            {
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="enableDragWithCtr"></param>
+                public AxisDrag(bool enableDragWithCtr)
+                {
+                    enableDragWithCtr_ = enableDragWithCtr;
+                }
+
+                private bool enableDragWithCtr_ = false;
+
+                private Axis axis_ = null;
+                private bool doing_ = false;
+                private Point lastPoint_ = new Point();
+                private PhysicalAxis physicalAxis_ = null;
+                private Point startPoint_ = new Point();
+
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseDown(MouseEventArgs e, Control ctr)
+                {
+                    // if the mouse is inside the plot area [the tick marks are here and part of the 
+                    // axis], then don't invoke drag. 
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+                    if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < ps.PlotAreaBoundingBoxCache.Right &&
+                        e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < ps.PlotAreaBoundingBoxCache.Bottom)
+                    {
+                        return false;
+                    }
+
+                    if ((e.Button == MouseButtons.Left))
+                    {
+                        // see if hit with axis.
+                        ArrayList objects = ps.HitTest(new Point(e.X, e.Y));
+
+                        foreach (object o in objects)
+                        {
+                            if (o is NPlot.Axis)
+                            {
+                                doing_ = true;
+                                axis_ = (Axis)o;
+
+                                PhysicalAxis[] physicalAxisList = new PhysicalAxis[] { ps.PhysicalXAxis1Cache, ps.PhysicalXAxis2Cache, ps.PhysicalYAxis1Cache, ps.PhysicalYAxis2Cache };
+
+                                if (ps.PhysicalXAxis1Cache.Axis == axis_)
+                                    physicalAxis_ = ps.PhysicalXAxis1Cache;
+                                else if (ps.PhysicalXAxis2Cache.Axis == axis_)
+                                    physicalAxis_ = ps.PhysicalXAxis2Cache;
+                                else if (ps.PhysicalYAxis1Cache.Axis == axis_)
+                                    physicalAxis_ = ps.PhysicalYAxis1Cache;
+                                else if (ps.PhysicalYAxis2Cache.Axis == axis_)
+                                    physicalAxis_ = ps.PhysicalYAxis2Cache;
+
+                                lastPoint_ = startPoint_ = new Point(e.X, e.Y);
+
+                                return false;
+                            }
+                        }
+
+                    }
+
+					return false;
+                }
+
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                /// <param name="lastKeyEventArgs"></param>
+                public override bool DoMouseMove(MouseEventArgs e, Control ctr, KeyEventArgs lastKeyEventArgs)
+                {
+                    NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    // if dragging on axis to zoom.
+					if ((e.Button == MouseButtons.Left) && doing_ && physicalAxis_ != null)
+                    {
+						if (enableDragWithCtr_ && lastKeyEventArgs != null && lastKeyEventArgs.Control)
+						{
+						}
+						else
+						{
+							float dist =
+								(e.X - lastPoint_.X) +
+								(-e.Y + lastPoint_.Y);
+
+							lastPoint_ = new Point(e.X, e.Y);
+
+							if (dist > sensitivity_ / 3.0f)
+							{
+								dist = sensitivity_ / 3.0f;
+							}
+
+							PointF pMin = physicalAxis_.PhysicalMin;
+							PointF pMax = physicalAxis_.PhysicalMax;
+							double physicalWorldLength = Math.Sqrt((pMax.X - pMin.X) * (pMax.X - pMin.X) + (pMax.Y - pMin.Y) * (pMax.Y - pMin.Y));
+
+							float prop = (float)(physicalWorldLength * dist / sensitivity_);
+							prop *= 2;
+
+							((Windows.PlotSurface2D)ctr).CacheAxes();
+
+							float relativePosX = (startPoint_.X - pMin.X) / (pMax.X - pMin.X);
+							float relativePosY = (startPoint_.Y - pMin.Y) / (pMax.Y - pMin.Y);
+
+							if (float.IsInfinity(relativePosX) || float.IsNaN(relativePosX)) relativePosX = 0.0f;
+							if (float.IsInfinity(relativePosY) || float.IsNaN(relativePosY)) relativePosY = 0.0f;
+
+							PointF physicalWorldMin = pMin;
+							PointF physicalWorldMax = pMax;
+
+							physicalWorldMin.X += relativePosX * prop;
+							physicalWorldMax.X -= (1 - relativePosX) * prop;
+							physicalWorldMin.Y -= relativePosY * prop;
+							physicalWorldMax.Y += (1 - relativePosY) * prop;
+							
+							double newWorldMin = axis_.PhysicalToWorld(physicalWorldMin, pMin, pMax, false);
+							double newWorldMax = axis_.PhysicalToWorld(physicalWorldMax, pMin, pMax, false);
+							axis_.WorldMin = newWorldMin;
+							axis_.WorldMax = newWorldMax;
+							
+							((Windows.PlotSurface2D)ctr).InteractionOccured(this);
+							
+							return true;
+						}
+                    }
+				
+					return false;
+                }
+
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseUp(MouseEventArgs e, Control ctr)
+                {
+                    if (doing_)
+                    {
+                        doing_ = false;
+                        axis_ = null;
+						physicalAxis_ = null;
+                        lastPoint_ = new Point();
+                    }
+
+					return false;
+                }
+
+                private float sensitivity_ = 200.0f;
+                
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <value></value>
+                public float Sensitivity
+                {
+                    get
+                    {
+                        return sensitivity_;
+                    }
+                    set
+                    {
+                        sensitivity_ = value;
+                    }
+                }
+
+            }
+            #endregion
+            #region MouseWheelZoom
+            /// <summary>
+            /// 
+            /// </summary>
+            public class MouseWheelZoom : Interaction
+            {
+
+                private Point point_ = new Point(-1, -1);
+                //private bool mouseDown_ = false;
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseUp(MouseEventArgs e, Control ctr)
+                {
+                    //mouseDown_ = false;
+					return false;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+                public override bool DoMouseDown(MouseEventArgs e, Control ctr)
+                {
+                    //NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+                    //if (e.X > ps.PlotAreaBoundingBoxCache.Left && e.X < ps.PlotAreaBoundingBoxCache.Right &&
+                    //    e.Y > ps.PlotAreaBoundingBoxCache.Top && e.Y < ps.PlotAreaBoundingBoxCache.Bottom)
+                    //{
+                    //    point_.X = e.X;
+                    //    point_.Y = e.Y;
+                    //    mouseDown_ = true;
+                    //}
+					return false;
+                }
+
+                /// <summary>
+                /// 
+                /// </summary>
+                /// <param name="e"></param>
+                /// <param name="ctr"></param>
+				public override bool DoMouseWheel(MouseEventArgs e, Control ctr)
+				{
+					NPlot.PlotSurface2D ps = ((Windows.PlotSurface2D)ctr).Inner;
+
+					((Windows.PlotSurface2D)ctr).CacheAxes();
+
+					float delta = (float)e.Delta / (float)e.Delta;
+					delta *= sensitivity_;
+
+					Axis axis = null;
+					PointF pMin = PointF.Empty;
+					PointF pMax = PointF.Empty;
+					KeyEventArgs keyArgs = ((Windows.PlotSurface2D)ctr).lastKeyEventArgs_;
+					bool zoom = (keyArgs != null && keyArgs.Control);
+
+					if (keyArgs != null && keyArgs.Shift)     
+					{
+						axis = ps.YAxis1;
+						pMin = ps.PhysicalYAxis1Cache.PhysicalMin;
+						pMax = ps.PhysicalYAxis1Cache.PhysicalMax;
+					}
+					else            
+					{
+						axis = ps.XAxis1;
+						pMin = ps.PhysicalXAxis1Cache.PhysicalMin;
+						pMax = ps.PhysicalXAxis1Cache.PhysicalMax;
+					}
+					if (axis == null) return false;
+					
+					PointF physicalWorldMin = pMin;
+					PointF physicalWorldMax = pMax;
+					physicalWorldMin.X -= delta;
+					physicalWorldMax.X -= (zoom) ? -delta : delta;
+					physicalWorldMin.Y += delta;
+					physicalWorldMax.Y += (zoom) ? -delta : delta;
+					double newWorldMin = axis.PhysicalToWorld(physicalWorldMin, pMin, pMax, false);
+					double newWorldMax = axis.PhysicalToWorld(physicalWorldMax, pMin, pMax, false);
+					axis.WorldMin = newWorldMin;
+					axis.WorldMax = newWorldMax;
+					
+					((Windows.PlotSurface2D)ctr).InteractionOccured(this);
+				
+					return true;
+                }
+
+
+				/// <summary>
+				/// Number of screen pixels equivalent to one wheel step.
+				/// </summary>
+				public float Sensitivity			                 
+				{
+					get
+										                     
+					{
+						return sensitivity_;
+					}
+					set
+									                     
+					{
+						sensitivity_ = value;
+					}
+				}
+	            private float sensitivity_ = 60.0f;
+
+            }
+            #endregion
+
+        }
+
+        private ArrayList interactions_ = new ArrayList();
+
+
+        /// <summary>
+        /// Adds and interaction to the plotsurface that adds functionality that responds 
+        /// to a set of mouse / keyboard events. 
+        /// </summary>
+        /// <param name="i">the interaction to add.</param>
+        public void AddInteraction(Interactions.Interaction i)
+        {
+            interactions_.Add(i);
+        }
+
+
+		/// <summary>
+		/// Remove a previously added interaction
+		/// </summary>
+		/// <param name="i">interaction to remove</param>
+		public void RemoveInteraction(Interactions.Interaction i)             
+		{
+			interactions_.Remove(i);
+		}
+
+
+        /// <summary>
+        /// This is the signature of the function used for InteractionOccurred events.
+        /// 
+        /// TODO: expand this to include information about the event. 
+        /// </summary>
+        /// <param name="sender"></param>
+        public delegate void InteractionHandler(object sender);
+        
+
+        /// <summary>
+        /// Event is fired when an interaction happens with the plot that causes it to be modified.
+        /// </summary>
+        public event InteractionHandler InteractionOccured;
+
+        /// <summary>
+        /// Default function called when plotsurface modifying interaction occured. 
+        /// 
+        /// Override this, or add method to InteractionOccured event.
+        /// </summary>
+        /// <param name="sender"></param>
+        protected void OnInteractionOccured(object sender)
+        {
+            // do nothing.
+        }
+
+        /// <summary>
+        /// This is the signature of the function used for PreRefresh events.
+        /// </summary>
+        /// <param name="sender"></param>
+        public delegate void PreRefreshHandler(object sender);
+
+
+        /// <summary>
+        /// Event fired when we are about to paint.
+        /// </summary>
+        public event PreRefreshHandler PreRefresh;
+
+
+        /// <summary>
+        /// Default function called just before a refresh happens.
+        /// </summary>
+        /// <param name="sender"></param>
+        protected void OnPreRefresh(object sender)
+        {
+            // do nothing.
+        }
+
+
+		#region class PlotContextMenu
+		/// <summary>
+		/// Summary description for ContextMenu.
+		/// </summary>
+		public class PlotContextMenu
+		{
+
+			#region IPlotMenuItem
+			/// <summary>
+			/// elements of the MenuItems array list must implement this interface.
+			/// </summary>
+			public interface IPlotMenuItem
+			{
+				/// <summary>
+				/// Gets the Windows.Forms.MenuItem associated with the PlotMenuItem
+				/// </summary>
+				System.Windows.Forms.MenuItem MenuItem { get; }
+
+				/// <summary>
+				/// This method is called for each menu item before the menu is 
+				/// displayed. It is useful for implementing check marks, disabling
+				/// etc.
+				/// </summary>
+				/// <param name="plotContextMenu"></param>
+				void OnPopup( PlotContextMenu plotContextMenu );
+			}
+			#endregion
+			#region PlotMenuSeparator
+			/// <summary>
+			/// A plot menu item for separators.
+			/// </summary>
+			public class PlotMenuSeparator : IPlotMenuItem
+			{
+
+				/// <summary>
+				/// Constructor
+				/// </summary>
+				/// <param name="index"></param>
+				public PlotMenuSeparator( int index )
+				{
+					menuItem_ = new System.Windows.Forms.MenuItem();
+					index_ = index;
+
+					menuItem_.Index = index_;
+					menuItem_.Text = "-";
+				}
+
+				private int index_;
+
+				/// <summary>
+				/// Index of this menu item in the menu.
+				/// </summary>
+				public int Index
+				{
+					get
+					{
+						return index_;
+					}
+				}
+
+				private System.Windows.Forms.MenuItem menuItem_;
+				/// <summary>
+				/// The Windows.Forms.MenuItem associated with this IPlotMenuItem
+				/// </summary>
+				public System.Windows.Forms.MenuItem MenuItem
+				{
+					get
+					{
+						return menuItem_;
+					}
+				}
+
+				/// <summary>
+				/// 
+				/// </summary>
+				/// <param name="plotContextMenu"></param>
+				public void OnPopup( PlotContextMenu plotContextMenu )
+				{
+					// do nothing.
+				}
+
+			}
+			#endregion
+			#region PlotMenuItem
+			/// <summary>
+			/// A Plot menu item suitable for specifying basic menu items
+			/// </summary>
+			public class PlotMenuItem : IPlotMenuItem
+			{
+
+				/// <summary>
+				/// Constructor
+				/// </summary>
+				/// <param name="text">Menu item text</param>
+				/// <param name="index">Index in the manu</param>
+				/// <param name="callback">EventHandler to call if menu selected.</param>
+				public PlotMenuItem( string text, int index, EventHandler callback )
+				{
+					text_ = text;
+					index_ = index;
+					callback_ = callback;
+
+					menuItem_ = new System.Windows.Forms.MenuItem();
+					
+					menuItem_.Index = index;
+					menuItem_.Text = text;
+					menuItem_.Click += new System.EventHandler(callback);
+
+				}
+
+				private string text_;
+				/// <summary>
+				/// The text to put in the menu for this menu item.
+				/// </summary>
+				public string Text
+				{
+					get
+					{
+						return text_;
+					}
+				}
+
+				private int index_;
+				/// <summary>
+				/// Index of this menu item in the menu.
+				/// </summary>
+				public int Index
+				{
+					get
+					{
+						return index_;
+					}
+				}
+
+				private EventHandler callback_;
+				/// <summary>
+				/// EventHandler to call if menu selected.
+				/// </summary>
+				public EventHandler Callback
+				{
+					get
+					{
+						return callback_;
+					}
+				}
+
+				private System.Windows.Forms.MenuItem menuItem_;
+				/// <summary>
+				/// The Windows.Forms.MenuItem associated with this IPlotMenuItem
+				/// </summary>
+				public System.Windows.Forms.MenuItem MenuItem
+				{
+					get
+					{
+						return menuItem_;
+					}
+				}
+
+				/// <summary>
+				/// Called before menu drawn.
+				/// </summary>
+				/// <param name="plotContextMenu">The plot menu this item is a member of.</param>
+				public virtual void OnPopup( PlotContextMenu plotContextMenu )
+				{
+					// do nothing.
+				}
+
+			}
+			#endregion
+			#region PlotZoomBackMenuItem
+			/// <summary>
+			/// A Plot Menu Item that provides necessary functionality for the
+			/// zoom back menu item (graying out if zoomed right out in addition
+			/// to basic functionality).
+			/// </summary>
+			public class PlotZoomBackMenuItem : PlotMenuItem
+			{
+
+				/// <summary>
+				/// Constructor
+				/// </summary>
+				/// <param name="text">Text associated with this item in the menu.</param>
+				/// <param name="index">Index of this item in the menu.</param>
+				/// <param name="callback">EventHandler to call when menu item is selected.</param>
+				public PlotZoomBackMenuItem( string text, int index, EventHandler callback )
+					: base( text, index, callback )
+				{
+				}
+
+				/// <summary>
+				/// Called before menu drawn.
+				/// </summary>
+				/// <param name="plotContextMenu">The plot menu this item is a member of.</param>
+				public override void OnPopup( PlotContextMenu plotContextMenu )
+				{
+					this.MenuItem.Enabled = plotContextMenu.plotSurface2D_.xAxis1ZoomCache_ != null;
+				}
+
+			}
+			#endregion
+			#region PlotShowCoordinatesMenuItem
+			/// <summary>
+			/// A Plot Menu Item that provides necessary functionality for the
+			/// show coordinates menu item (tick mark toggle in addition to basic
+			/// functionality).
+			/// </summary>
+			public class PlotShowCoordinatesMenuItem : PlotMenuItem
+			{
+
+				/// <summary>
+				/// Constructor
+				/// </summary>
+				/// <param name="text">Text associated with this item in the menu.</param>
+				/// <param name="index">Index of this item in the menu.</param>
+				/// <param name="callback">EventHandler to call when menu item is selected.</param>
+				public PlotShowCoordinatesMenuItem( string text, int index, EventHandler callback )
+					: base( text, index, callback )
+				{
+				}
+
+				/// <summary>
+				/// Called before menu drawn.
+				/// </summary>
+				/// <param name="plotContextMenu">The plot menu this item is a member of.</param>
+				public override void OnPopup( PlotContextMenu plotContextMenu )
+				{
+					this.MenuItem.Checked = plotContextMenu.plotSurface2D_.ShowCoordinates;
+				}
+			}
+			#endregion
+
+			private System.Windows.Forms.ContextMenu rightMenu_ = null;
+			private ArrayList menuItems_ = null;
+
+
+			/// <summary>
+			/// Gets an arraylist of all PlotMenuItems that comprise the
+			/// menu. If this list is changed, this class must be told to
+			/// update using the Update method.
+			/// </summary>
+			public ArrayList MenuItems
+			{
+				get
+				{
+					return menuItems_;
+				}
+			}
+
+			/// <summary>
+			/// The PlotSurface2D associated with the context menu. Generally, the user
+			/// should not set this. It is used internally by PlotSurface2D.
+			/// </summary>
+			public Windows.PlotSurface2D PlotSurface2D
+			{
+				set
+				{
+					this.plotSurface2D_ = value;
+				}
+			}
+
+			/// <summary>
+			/// The PlotSurface2D associated with the context menu. Classes inherited
+			/// from PlotContextMenu will likely use this to implement their functionality.
+			/// </summary>
+			protected Windows.PlotSurface2D plotSurface2D_;
+
+			
+			/// <summary>
+			/// Sets the context menu according to the IPlotMenuItem's in the provided
+			/// ArrayList. The current menu items can be obtained using the MenuItems
+			/// property and extended if desired.
+			/// </summary>
+			/// <param name="menuItems"></param>
+			public void SetMenuItems(ArrayList menuItems)
+			{
+				this.menuItems_ = menuItems;
+
+				this.rightMenu_ = new System.Windows.Forms.ContextMenu();
+			
+				foreach (IPlotMenuItem item in menuItems_)
+				{
+					this.rightMenu_.MenuItems.Add( item.MenuItem );
+				}
+
+				this.rightMenu_.Popup += new System.EventHandler(this.rightMenu__Popup);
+			}
+
+
+			/// <summary>
+			/// Constructor creates
+			/// </summary>
+			public PlotContextMenu()
+			{
+				ArrayList menuItems = new ArrayList();
+
+				menuItems = new ArrayList();
+				menuItems.Add( new PlotZoomBackMenuItem( "Original Dimensions", 0, new EventHandler(this.mnuOriginalDimensions_Click) ) );
+				menuItems.Add( new PlotShowCoordinatesMenuItem( "Show World Coordinates", 1, new EventHandler(this.mnuDisplayCoordinates_Click) ) ); 
+				menuItems.Add( new PlotMenuSeparator(2) );
+				menuItems.Add( new PlotMenuItem( "Print", 3, new EventHandler(this.mnuPrint_Click )) );
+				menuItems.Add( new PlotMenuItem( "Print Preview", 4, new EventHandler(this.mnuPrintPreview_Click) ) );
+				menuItems.Add( new PlotMenuItem( "Copy To Clipboard", 5, new EventHandler(this.mnuCopyToClipboard_Click) ) );
+				menuItems.Add( new PlotMenuItem( "Copy Data To Clipboard", 6, new EventHandler(this.mnuCopyDataToClipboard_Click) ) );
+
+				this.SetMenuItems( menuItems );
+			}
+
+
+			private void mnuOriginalDimensions_Click(object sender, System.EventArgs e)
+			{
+				plotSurface2D_.OriginalDimensions();
+			}
+
+			private void mnuCopyToClipboard_Click(object sender, System.EventArgs e) 
+			{
+				plotSurface2D_.CopyToClipboard();
+			}
+
+			private void mnuCopyDataToClipboard_Click(object sender, System.EventArgs e) 
+			{
+				plotSurface2D_.CopyDataToClipboard();
+			}
+
+			private void mnuPrint_Click(object sender, System.EventArgs e) 
+			{
+				plotSurface2D_.Print( false );
+			}
+
+			private void mnuPrintPreview_Click(object sender, System.EventArgs e) 
+			{
+				plotSurface2D_.Print( true );
+			}
+
+			private void mnuDisplayCoordinates_Click(object sender, System.EventArgs e)
+			{
+				plotSurface2D_.ShowCoordinates = !plotSurface2D_.ShowCoordinates;
+			}
+
+			private void rightMenu__Popup(object sender, System.EventArgs e)
+			{
+				foreach (IPlotMenuItem item in menuItems_)
+				{
+					item.OnPopup( this );
+				}
+			}
+
+			/// <summary>
+			/// Gets the Windows.Forms context menu managed by this object.
+			/// </summary>
+			public System.Windows.Forms.ContextMenu Menu
+			{
+				get
+				{
+					return rightMenu_;
+				}
+			}
+
+		}
+		#endregion
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		protected override void Dispose( bool disposing )
+		{
+			if( disposing )
+			{
+				if( components != null )
+					components.Dispose();
+			}
+			base.Dispose( disposing );
+		}
+
+		private System.ComponentModel.IContainer components;
+
+	}
+
+}
diff --git a/nplot/nplot/Windows.PlotSurface2D.resx b/nplot/nplot/Windows.PlotSurface2D.resx
new file mode 100644
index 0000000..0e10f7d
--- /dev/null
+++ b/nplot/nplot/Windows.PlotSurface2D.resx
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<root>
+	<!-- 
+    Microsoft ResX Schema 
+    
+    Version 1.3
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">1.3</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1">this is my long string</data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        [base64 mime encoded serialized .NET Framework object]
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        [base64 mime encoded string representing a byte array form of the .NET Framework object]
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used forserialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"; xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+		<xsd:element name="root" msdata:IsDataSet="true">
+			<xsd:complexType>
+				<xsd:choice maxOccurs="unbounded">
+					<xsd:element name="data">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+							</xsd:sequence>
+							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+						</xsd:complexType>
+					</xsd:element>
+					<xsd:element name="resheader">
+						<xsd:complexType>
+							<xsd:sequence>
+								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+							</xsd:sequence>
+							<xsd:attribute name="name" type="xsd:string" use="required" />
+						</xsd:complexType>
+					</xsd:element>
+				</xsd:choice>
+			</xsd:complexType>
+		</xsd:element>
+	</xsd:schema>
+	<resheader name="resmimetype">
+		<value>text/microsoft-resx</value>
+	</resheader>
+	<resheader name="version">
+		<value>1.3</value>
+	</resheader>
+	<resheader name="reader">
+		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+	</resheader>
+	<resheader name="writer">
+		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+	</resheader>
+	<data name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+		<value>False</value>
+	</data>
+	<data name="$this.Name">
+		<value>PlotSurface2D</value>
+	</data>
+</root>
\ No newline at end of file
diff --git a/nplot/nplot/Windows.PlotSurface2Dnew.cs b/nplot/nplot/Windows.PlotSurface2Dnew.cs
new file mode 100644
index 0000000..71fcd23
--- /dev/null
+++ b/nplot/nplot/Windows.PlotSurface2Dnew.cs
@@ -0,0 +1,155 @@
+// ******** experimental ******** 
+
+/*
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+using System.IO;
+
+namespace NPlot.Windows
+{
+
+	/// <summary>
+	/// Experimental
+	/// </summary>
+	public class PlotSurface2Dnew : System.Windows.Forms.UserControl, IPlotSurface2Dnew
+	{
+		/// <summary>
+		/// Required designer variable.
+		/// </summary>
+		private System.ComponentModel.Container components = null;
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public PlotSurface2Dnew()
+		{
+			// This call is required by the Windows.Forms Form Designer.
+			InitializeComponent();
+
+			// double buffer, and update when resize.
+			base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
+			base.SetStyle(ControlStyles.DoubleBuffer, true);
+			base.SetStyle(ControlStyles.UserPaint, true);
+			base.ResizeRedraw = true;
+		}
+
+		/// <summary>
+		/// Clean up any resources being used.
+		/// </summary>
+		protected override void Dispose( bool disposing )
+		{
+			if( disposing )
+			{
+				if( components != null )
+					components.Dispose();
+			}
+			base.Dispose( disposing );
+		}
+
+		#region Component Designer generated code
+		/// <summary>
+		/// Required method for Designer support - do not modify 
+		/// the contents of this method with the code editor.
+		/// </summary>
+		private void InitializeComponent()
+		{
+			components = new System.ComponentModel.Container();
+		}
+		#endregion
+
+		private NPlot.PlotSurface2Dnew Inner = new NPlot.PlotSurface2Dnew();
+
+		/// <summary>
+		/// Experimental
+		/// </summary>
+		public void SetDefinition( )
+		{
+			Inner.SetDefinition( );
+		}
+
+		/// <summary>
+		/// the paint event callback.
+		/// </summary>
+		/// <param name="pe">paint event arguments - used to get graphics surface to draw on.</param>
+		protected override void OnPaint( PaintEventArgs pe )
+		{
+			Graphics g = pe.Graphics;
+			
+			Rectangle border = new Rectangle( 0, 0, this.Width, this.Height );
+			
+			if ( g == null ) 
+			{
+				ErrorHandler.Instance.CriticalError( "graphics context null" );
+			}
+						
+			if ( border == Rectangle.Empty )
+			{
+				ErrorHandler.Instance.CriticalError( "Control has zero extent" );
+				return;
+			}
+
+			this.Draw( g, border );
+
+			base.OnPaint(pe);
+		}
+
+
+		/// <summary>
+		/// Draws the plot surface on the supplied graphics surface [not the control surface].
+		/// </summary>
+		/// <param name="g">The graphics surface on which to draw</param>
+		/// <param name="bounds">A bounding box on this surface that denotes the area on the
+		/// surface to confine drawing to.</param>
+		public void Draw( Graphics g, Rectangle bounds )
+		{
+			try 
+			{
+				Inner.Draw( g, bounds );
+			} 
+			catch (Exception ex) 
+			{
+				System.Diagnostics.Debugger.Log( 1, "", "Exception drawing plot:" + ex + "\r\n" );
+			}
+		}
+
+
+		/// <summary>
+		/// Padding of this width will be left between what is drawn and the control border.
+		/// </summary>
+		[
+		Category("PlotSurface2D"),
+		Description("Padding of this width will be left between what is drawn and the control border."),
+		Browsable(true)
+		]
+		public int Padding
+		{
+			get
+			{
+				return Inner.Padding;
+			}
+			set
+			{
+				Inner.Padding = value;
+			}
+		}
+
+
+		/// <summary>
+		/// Adds a drawable object to the plot surface against the specified axes. If
+		/// the object is an IPlot, the PlotSurface2D axes will also be updated.
+		/// </summary>
+		/// <param name="p">the IDrawable object to add to the plot surface</param>
+		/// <param name="xAxis">the x-axis to add the plot against.</param>
+		/// <param name="yAxis">the y-axis to add the plot against.</param>am>
+		public void Add( IDrawable p, string xAxis, string yAxis )
+		{
+			this.Inner.Add( p, xAxis, yAxis );
+		}
+
+	}
+}
+*/
diff --git a/nplot/nplot/house_rules.txt b/nplot/nplot/house_rules.txt
new file mode 100644
index 0000000..36a2d6d
--- /dev/null
+++ b/nplot/nplot/house_rules.txt
@@ -0,0 +1,94 @@
+House Rules and random notes:
+
+Note: I haven't followed these always. They reflect my current preferences, which 
+vary a little from time to time. Anything reasonably close is good enough. 
+
+
+o Classes begin with a capital letter and each world is capitalized. Eg:
+
+  HistogramPlot
+
+
+o Class member functions begin with capital letter and each word is capitalized. Eg:
+
+  public void DrawTicks()
+
+
+o Class member variables begin with a lower case letter, each word there-after capitalized.
+  Trailing underscore. Eg:
+  
+  float worldMax_
+
+
+o variables begin with a lower case letter, each world there-after capitalized. 
+  This includes variables in a argumant list. Eg:
+
+  float worldMax
+  public void DrawTicks( float myArgument )
+
+
+o use ++i over i++. (habbit from c++, ++i never less efficient than i++, sometimes more).
+
+
+o always use braces. Do:
+  
+  if ( a == 3 )
+  {
+    return;
+  }
+
+  not
+  
+  if ( a == 3 )
+	return;
+	
+
+o add your name to the copyright list of a file if you do significant modifications 
+  to it, or create it.
+
+
+o Don't use hungarian notation. :-). It's not the done thing in C#, and the other
+  rules here allow differentiation between identifier types.
+
+
+o braces like this:
+  
+  if (a == 3)
+  {
+     Console.WriteLine( "Hello world\n" );
+  }
+
+  not like this:
+ 
+  if (a == 3) {
+     Console.WriteLine( "Hello world\n" );
+  }
+
+
+o spaces like this:
+
+  if (a == 3) 
+
+  not like this:
+
+  if( a == 3 )
+
+  or this:
+
+  if (a==3)
+
+  or this:
+ 
+  if(a==3) 
+
+o Comments: 
+
+  (1) Avoid obvious comments, they annoy me.
+  (2) Make sure you _really_ understand what is going on before commenting. No comment is much           much better than an incorrect comment.
+  (3) Use comments to leave notes to other coders (in particular me), so that I can quickly 
+          see what has changed. I'll feel free to delete them.
+
+o CVS:
+
+  Don't check in code that doesn't compile and run.
+  
\ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
index cec97ba..4f81629 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -35,13 +35,7 @@ COMMONAPPLICATIONDATA_ROOT = \
  
 
 PROGRAMFILES = \
-	$(CHRONOJUMP_MINI_EXE)\
-	$(NPLOT_DLL)\
-	$(NPLOT_DLL_CONFIG)\
-	$(NPLOT_GTK_DLL)\
-	$(NPLOT_GTK_DLL_CONFIG)
-
-
+	$(CHRONOJUMP_MINI_EXE)
 
 LINUX_DESKTOPAPPLICATIONS = \
 	$(CHRONOJUMP_DESKTOP) 
@@ -315,8 +309,8 @@ REFERENCES =  \
 
 
 DLL_REFERENCES =  \
-	../build/data/linux_dlls/NPlot.dll\
-	../build/data/linux_dlls/NPlot.Gtk.dll
+	../nplot/nplot/bin/NPlot.dll\
+	../nplot/nplot-gtk/bin/NPlot.Gtk.dll
 
 
 #CLEANFILES = $(COMMONAPPLICATIONDATA_ROOT) $(COMMONAPPLICATIONDATAROOT_IMAGES) $(COMMONAPPLICATIONDATAROOT_LOGO) $(PROGRAMFILES) $(LINUX_DESKTOPAPPLICATIONS) $(BINARIES) 
@@ -333,25 +327,11 @@ CHRONOJUMP_DESKTOP = $(BUILD_DIR)/chronojump.desktop
 CHRONOJUMP_EXE_MDB = $(BUILD_DIR)/ChronoJump.exe.mdb
 CHRONOJUMP=$(BUILD_DIR)/chronojump
 CHRONOJUMP_MINI=$(BUILD_DIR)/chronojump_mini
-NPLOT_DLL_SOURCE=../build/data/linux_dlls/NPlot.dll
-NPLOT_GTK_DLL_SOURCE=../build/data/linux_dlls/NPlot.Gtk.dll
-NPLOT_DLL_CONFIG_SOURCE=../build/data/linux_dlls/NPlot.dll.config
-NPLOT_GTK_DLL_CONFIG_SOURCE=../build/data/linux_dlls/NPlot.Gtk.dll.config
-NPLOT_DLL=$(BUILD_DIR)/NPlot.dll
-NPLOT_GTK_DLL=$(BUILD_DIR)/NPlot.Gtk.dll
-NPLOT_DLL_CONFIG=$(BUILD_DIR)/NPlot.dll.config
-NPLOT_GTK_DLL_CONFIG=$(BUILD_DIR)/NPlot.Gtk.dll.config
 
 $(eval $(call emit-deploy-wrapper,CHRONOJUMP,chronojump,x))
 $(eval $(call emit-deploy-wrapper,CHRONOJUMP_MINI,chronojump_mini,x))
 $(eval $(call emit-deploy-target,CHRONOJUMP_DESKTOP))
 $(eval $(call emit-deploy-target,CHRONOJUMP_LOGO))
-$(eval $(call emit-deploy-target,NPLOT_DLL))
-$(eval $(call emit-deploy-target,NPLOT_GTK_DLL))
-$(eval $(call emit-deploy-target,NPLOT_DLL_CONFIG))
-$(eval $(call emit-deploy-target,NPLOT_GTK_DLL_CONFIG))
-
-
 
 
 $(eval $(call emit_resgen_targets))



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]