[chronojump] [autotools] Compile NPlot from sources to avoid using precompiled binaries
- From: Andoni Morales Alastruey <amorales src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [chronojump] [autotools] Compile NPlot from sources to avoid using precompiled binaries
- Date: Wed, 26 Aug 2009 23:22:37 +0000 (UTC)
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]