935 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			935 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //============================================================================
 | |
| //PointPairList Class
 | |
| //Copyright ?2004  Jerry Vos
 | |
| //
 | |
| //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
 | |
| //=============================================================================
 | |
| 
 | |
| using System;
 | |
| using System.Drawing;
 | |
| using System.Collections.Generic;
 | |
| 
 | |
| namespace DrawGraph
 | |
| {
 | |
| 	/// <summary>
 | |
| 	/// A collection class containing a list of <see cref="PointPair"/> objects
 | |
| 	/// that define the set of points to be displayed on the curve.
 | |
| 	/// </summary>
 | |
| 	/// <seealso cref="BasicArrayPointList" />
 | |
| 	/// <seealso cref="IPointList" />
 | |
| 	/// 
 | |
| 	/// <author> Jerry Vos based on code by John Champion
 | |
| 	/// modified by John Champion</author>
 | |
| 	/// <version> $Revision: 3.36 $ $Date: 2007/02/18 05:51:54 $ </version>
 | |
| 	[Serializable]
 | |
| 	public class PointPairList : List<PointPair>, IPointList, IPointListEdit
 | |
| 	{
 | |
| 	#region Fields
 | |
| 		/// <summary>Private field to maintain the sort status of this
 | |
| 		/// <see cref="PointPairList"/>.  Use the public property
 | |
| 		/// <see cref="Sorted"/> to access this value.
 | |
| 		/// </summary>
 | |
| 		protected bool _sorted = true;
 | |
| 	#endregion
 | |
| 
 | |
| 	#region Properties
 | |
| /*		/// <summary>
 | |
| 		/// Indexer to access the specified <see cref="PointPair"/> object by
 | |
| 		/// its ordinal position in the list.
 | |
| 		/// </summary>
 | |
| 		/// <param name="index">The ordinal position (zero-based) of the
 | |
| 		/// <see cref="PointPair"/> object to be accessed.</param>
 | |
| 		/// <value>A <see cref="PointPair"/> object reference.</value>
 | |
| 		public PointPair this[ int index ]  
 | |
| 		{
 | |
| 			get { return (PointPair) List[index]; }
 | |
| 			set { List[index] = value; }
 | |
| 		}
 | |
| */
 | |
| 		/// <summary>
 | |
| 		/// true if the list is currently sorted.
 | |
| 		/// </summary>
 | |
| 		/// <seealso cref="Sort()"/>
 | |
| 		public bool Sorted
 | |
| 		{
 | |
| 			get { return _sorted; }
 | |
| 		}
 | |
| 	#endregion
 | |
| 
 | |
| 	#region Constructors
 | |
| 		/// <summary>
 | |
| 		/// Default constructor for the collection class
 | |
| 		/// </summary>
 | |
| 		public PointPairList()
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Constructor to initialize the PointPairList from two arrays of
 | |
| 		/// type double.
 | |
| 		/// </summary>
 | |
| 		public PointPairList( double[] x, double[] y )
 | |
| 		{
 | |
| 			Add( x, y );
 | |
| 
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Constructor to initialize the PointPairList from an IPointList
 | |
| 		/// </summary>
 | |
| 		public PointPairList( IPointList list )
 | |
| 		{
 | |
| 			int count = list.Count;
 | |
| 			for ( int i = 0; i < count; i++ )
 | |
| 				Add( list[i] );
 | |
| 
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Constructor to initialize the PointPairList from three arrays of
 | |
| 		/// type double.
 | |
| 		/// </summary>
 | |
| 		public PointPairList( double[] x, double[] y, double[] baseVal )
 | |
| 		{
 | |
| 			Add( x, y, baseVal );
 | |
| 			
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// The Copy Constructor
 | |
| 		/// </summary>
 | |
| 		/// <param name="rhs">The PointPairList from which to copy</param>
 | |
| 		public PointPairList( PointPairList rhs )
 | |
| 		{
 | |
| 			Add( rhs );
 | |
| 
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Implement the <see cref="ICloneable" /> interface in a typesafe manner by just
 | |
| 		/// calling the typed version of <see cref="Clone" />
 | |
| 		/// </summary>
 | |
| 		/// <returns>A deep copy of this object</returns>
 | |
| 		object ICloneable.Clone()
 | |
| 		{
 | |
| 			return this.Clone();
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Typesafe, deep-copy clone method.
 | |
| 		/// </summary>
 | |
| 		/// <returns>A new, independent copy of this class</returns>
 | |
| 		public PointPairList Clone()
 | |
| 		{
 | |
| 			return new PointPairList( this );
 | |
| 		}
 | |
| 
 | |
| 	#endregion
 | |
| 
 | |
| 	#region Methods
 | |
| 		/// <summary>
 | |
| 		/// Add a <see cref="PointPair"/> object to the collection at the end of the list.
 | |
| 		/// </summary>
 | |
| 		/// <param name="point">The <see cref="PointPair"/> object to
 | |
| 		/// be added</param>
 | |
| 		/// <returns>The zero-based ordinal index where the point was added in the list.</returns>
 | |
| 		public new void Add( PointPair point )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			base.Add( new PointPair( point ) );
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a <see cref="PointPairList"/> object to the collection at the end of the list.
 | |
| 		/// </summary>
 | |
| 		/// <param name="pointList">A reference to the <see cref="PointPairList"/> object to
 | |
| 		/// be added</param>
 | |
| 		/// <returns>The zero-based ordinal index where the last point was added in the list,
 | |
| 		/// or -1 if no points were added.</returns>
 | |
| 		public void Add( PointPairList pointList )
 | |
| 		{
 | |
| 			foreach ( PointPair point in pointList )
 | |
| 			Add( point );
 | |
| 				
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a set of points to the PointPairList from two arrays of type double.
 | |
| 		/// If either array is null, then a set of ordinal values is automatically
 | |
| 		/// generated in its place (see <see cref="AxisType.Ordinal"/>.
 | |
| 		/// If the arrays are of different size, then the larger array prevails and the
 | |
| 		/// smaller array is padded with <see cref="PointPairBase.Missing"/> values.
 | |
| 		/// </summary>
 | |
| 		/// <param name="x">A double[] array of X values</param>
 | |
| 		/// <param name="y">A double[] array of Y values</param>
 | |
| 		/// <returns>The zero-based ordinal index where the last point was added in the list,
 | |
| 		/// or -1 if no points were added.</returns>
 | |
| 		public void Add( double[] x, double[] y )
 | |
| 		{
 | |
| 			int 		len = 0;
 | |
| 			
 | |
| 			if ( x != null )
 | |
| 				len = x.Length;
 | |
| 			if ( y != null && y.Length > len )
 | |
| 				len = y.Length;
 | |
| 			
 | |
| 			for ( int i=0; i<len; i++ )
 | |
| 			{
 | |
| 				PointPair	point = new PointPair( 0, 0, 0 );
 | |
| 				if ( x == null )
 | |
| 					point.X = (double) i + 1.0;
 | |
| 				else if ( i < x.Length )
 | |
| 					point.X = x[i];
 | |
| 				else
 | |
| 					point.X = PointPair.Missing;
 | |
| 					
 | |
| 				if ( y == null )
 | |
| 					point.Y = (double) i + 1.0;
 | |
| 				else if ( i < y.Length )
 | |
| 					point.Y = y[i];
 | |
| 				else
 | |
| 					point.Y = PointPair.Missing;
 | |
| 					
 | |
| 				base.Add( point );
 | |
| 			}
 | |
| 			
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a set of points to the <see cref="PointPairList"/> from three arrays of type double.
 | |
| 		/// If the X or Y array is null, then a set of ordinal values is automatically
 | |
| 		/// generated in its place (see <see cref="AxisType.Ordinal"/>.  If the <see paramref="baseVal"/>
 | |
| 		/// is null, then it is set to zero.
 | |
| 		/// If the arrays are of different size, then the larger array prevails and the
 | |
| 		/// smaller array is padded with <see cref="PointPairBase.Missing"/> values.
 | |
| 		/// </summary>
 | |
| 		/// <param name="x">A double[] array of X values</param>
 | |
| 		/// <param name="y">A double[] array of Y values</param>
 | |
| 		/// <param name="z">A double[] array of Z or lower-dependent axis values</param>
 | |
| 		/// <returns>The zero-based ordinal index where the last point was added in the list,
 | |
| 		/// or -1 if no points were added.</returns>
 | |
| 		public void Add( double[] x, double[] y, double[] z )
 | |
| 		{
 | |
| 			int 		len = 0;
 | |
| 			
 | |
| 			if ( x != null )
 | |
| 				len = x.Length;
 | |
| 			if ( y != null && y.Length > len )
 | |
| 				len = y.Length;
 | |
| 			if ( z != null && z.Length > len )
 | |
| 				len = z.Length;
 | |
| 						
 | |
| 			for ( int i=0; i<len; i++ )
 | |
| 			{
 | |
| 				PointPair point = new PointPair();
 | |
| 
 | |
| 				if ( x == null )
 | |
| 					point.X = (double) i + 1.0;
 | |
| 				else if ( i < x.Length )
 | |
| 					point.X = x[i];
 | |
| 				else
 | |
| 					point.X = PointPair.Missing;
 | |
| 					
 | |
| 				if ( y == null )
 | |
| 					point.Y = (double) i + 1.0;
 | |
| 				else if ( i < y.Length )
 | |
| 					point.Y = y[i];
 | |
| 				else
 | |
| 					point.Y = PointPair.Missing;
 | |
| 					
 | |
| 				if ( z == null )
 | |
| 					point.Z = (double) i + 1.0;
 | |
| 				else if ( i < z.Length )
 | |
| 					point.Z = z[i];
 | |
| 				else
 | |
| 					point.Z = PointPair.Missing;
 | |
| 					
 | |
| 				base.Add( point );
 | |
| 			}
 | |
| 			
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a single point to the <see cref="PointPairList"/> from values of type double.
 | |
| 		/// </summary>
 | |
| 		/// <param name="x">The X value</param>
 | |
| 		/// <param name="y">The Y value</param>
 | |
| 		/// <returns>The zero-based ordinal index where the point was added in the list.</returns>
 | |
| 		public void Add( double x, double y )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			PointPair	point = new PointPair( x, y );
 | |
| 			base.Add( point );
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a single point to the <see cref="PointPairList"/> from values of type double.
 | |
| 		/// </summary>
 | |
| 		/// <param name="x">The X value</param>
 | |
| 		/// <param name="y">The Y value</param>
 | |
| 		/// <param name="tag">The Tag value for the PointPair</param>
 | |
| 		/// <returns>The zero-based ordinal index where the point was added in the list.</returns>
 | |
| 		public void Add( double x, double y, string tag )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			PointPair	point = new PointPair( x, y, tag );
 | |
| 			base.Add( point );
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a single point to the <see cref="PointPairList"/> from values of type double.
 | |
| 		/// </summary>
 | |
| 		/// <param name="x">The X value</param>
 | |
| 		/// <param name="y">The Y value</param>
 | |
| 		/// <param name="z">The Z or lower dependent axis value</param>
 | |
| 		/// <returns>The zero-based ordinal index where the point was added
 | |
| 		/// in the list.</returns>
 | |
| 		public void Add( double x, double y, double z )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			PointPair point = new PointPair( x, y, z );
 | |
| 			base.Add( point );
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a single point to the <see cref="PointPairList"/> from values of type double.
 | |
| 		/// </summary>
 | |
| 		/// <param name="x">The X value</param>
 | |
| 		/// <param name="y">The Y value</param>
 | |
| 		/// <param name="z">The Z or lower dependent axis value</param>
 | |
| 		/// <param name="tag">The Tag value for the PointPair</param>
 | |
| 		/// <returns>The zero-based ordinal index where the point was added
 | |
| 		/// in the list.</returns>
 | |
| 		public void Add( double x, double y, double z, string tag )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			PointPair point = new PointPair( x, y, z, tag );
 | |
| 			base.Add( point );
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a <see cref="PointPair"/> object to the collection at the specified,
 | |
| 		/// zero-based, index location.
 | |
| 		/// </summary>
 | |
| 		/// <param name="index">
 | |
| 		/// The zero-based ordinal index where the point is to be added in the list.
 | |
| 		/// </param>
 | |
| 		/// <param name="point">
 | |
| 		/// The <see cref="PointPair"/> object to be added.
 | |
| 		/// </param>
 | |
| 		public new void Insert( int index, PointPair point )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			base.Insert( index, point );
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a single point (from values of type double ) to the <see cref="PointPairList"/> at the specified,
 | |
| 		/// zero-based, index location.
 | |
| 		/// </summary>
 | |
| 		/// <param name="index">
 | |
| 		/// The zero-based ordinal index where the point is to be added in the list.
 | |
| 		/// </param>
 | |
| 		/// <param name="x">The X value</param>
 | |
| 		/// <param name="y">The Y value</param>
 | |
| 		public void Insert( int index, double x, double y )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			base.Insert( index, new PointPair( x, y ) );
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add a single point (from values of type double ) to the <see cref="PointPairList"/> at the specified,
 | |
| 		/// zero-based, index location.
 | |
| 		/// </summary>
 | |
| 		/// <param name="index">
 | |
| 		/// The zero-based ordinal index where the point is to be added in the list.
 | |
| 		/// </param>
 | |
| 		/// <param name="x">The X value</param>
 | |
| 		/// <param name="y">The Y value</param>
 | |
| 		/// <param name="z">The Z or lower dependent axis value</param>
 | |
| 		public void Insert( int index, double x, double y, double z )
 | |
| 		{
 | |
| 			_sorted = false;
 | |
| 			Insert( index, new PointPair( x, y, z ) );
 | |
| 		}
 | |
| 		/*
 | |
| 				/// <summary>
 | |
| 				/// Remove the specified <see cref="PointPair"/> object from the collection based
 | |
| 				/// the point values (must match exactly).
 | |
| 				/// </summary>
 | |
| 				/// <param name="pt">
 | |
| 				/// A <see cref="PointPair"/> that is to be removed by value.
 | |
| 				/// </param>
 | |
| 				/// <seealso cref="IList.Remove"/>
 | |
| 				public void Remove( PointPair pt )
 | |
| 				{
 | |
| 					List.Remove( pt );
 | |
| 				}
 | |
| 
 | |
| 				/// <summary>
 | |
| 				/// Return the zero-based position index of the specified
 | |
| 				/// <see cref="PointPair"/> in the collection.
 | |
| 				/// </summary>
 | |
| 				/// <param name="pt">The <see cref="PointPair"/> object that is to be found.
 | |
| 				/// </param>
 | |
| 				/// <returns>The zero-based index of the specified <see cref="PointPair"/>, or -1 if the <see cref="PointPair"/>
 | |
| 				/// is not in the list</returns>
 | |
| 				/// <seealso cref="IList.IndexOf"/>
 | |
| 				public int IndexOf( PointPair pt )
 | |
| 				{
 | |
| 					return List.IndexOf( pt );
 | |
| 				}
 | |
| 		*/
 | |
| 		/// <summary>
 | |
| 		/// Return the zero-based position index of the
 | |
| 		/// <see cref="PointPair"/> with the specified label <see cref="PointPair.Tag"/>.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>The <see cref="PointPair.Tag"/> object must be of type <see cref="String"/>
 | |
| 		/// for this method to find it.</remarks>
 | |
| 		/// <param name="label">The <see cref="String"/> label that is in the
 | |
| 		/// <see cref="PointPair.Tag"/> attribute of the item to be found.
 | |
| 		/// </param>
 | |
| 		/// <returns>The zero-based index of the specified <see cref="PointPair"/>,
 | |
| 		/// or -1 if the <see cref="PointPair"/> is not in the list</returns>
 | |
| 		public int IndexOfTag( string label )
 | |
| 		{
 | |
| 			int iPt = 0;
 | |
| 			foreach ( PointPair p in this )
 | |
| 			{
 | |
| 				if ( p.Tag is string && String.Compare( (string) p.Tag, label, true ) == 0 )
 | |
| 					return iPt;
 | |
| 				iPt++;
 | |
| 			}
 | |
| 
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Compare two <see cref="PointPairList"/> objects to see if they are equal.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>Equality is based on equal count of <see cref="PointPair"/> items, and
 | |
| 		/// each individual <see cref="PointPair"/> must be equal (as per the
 | |
| 		/// <see cref="PointPair.Equals"/> method.</remarks>
 | |
| 		/// <param name="obj">The <see cref="PointPairList"/> to be compared with for equality.</param>
 | |
| 		/// <returns>true if the <see cref="PointPairList"/> objects are equal, false otherwise.</returns>
 | |
| 		public override bool Equals( object obj )
 | |
| 		{
 | |
| 			PointPairList rhs = obj as PointPairList;
 | |
| 			if( this.Count != rhs.Count )
 | |
| 				return false;
 | |
| 
 | |
| 			for( int i=0; i<this.Count; i++ )
 | |
| 			{
 | |
| 				if( !this[i].Equals(rhs[i]) )
 | |
| 					return false;
 | |
| 			}
 | |
| 
 | |
| 			return true;
 | |
| 		}
 | |
| 		
 | |
| 		/// <summary>
 | |
| 		/// Return the HashCode from the base class.
 | |
| 		/// </summary>
 | |
| 		/// <returns></returns>
 | |
| 		public override int GetHashCode()
 | |
| 		{
 | |
| 			return base.GetHashCode ();
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Sorts the list according to the point x values. Will not sort the 
 | |
| 		/// list if the list is already sorted.
 | |
| 		/// </summary>
 | |
| 		/// <returns>If the list was sorted before sort was called</returns>
 | |
| 		public new bool Sort()
 | |
| 		{
 | |
| 			// if it is already sorted we don't have to sort again
 | |
| 			if ( _sorted )
 | |
| 				return true;
 | |
| 
 | |
| 			Sort( new PointPair.PointPairComparer( SortType.XValues ) );
 | |
| 			return false;
 | |
| 		}
 | |
| 		
 | |
| 		/// <summary>
 | |
| 		/// Sorts the list according to the point values . Will not sort the 
 | |
| 		/// list if the list is already sorted.
 | |
| 		/// </summary>
 | |
| 		/// <param name="type"></param>  The <see cref = "SortType"/>
 | |
| 		///used to determine whether the X or Y values will be used to sort
 | |
| 		///the list
 | |
| 		/// <returns>If the list was sorted before sort was called</returns>
 | |
| 		public bool Sort( SortType type)
 | |
| 		{
 | |
| 			// if it is already sorted we don't have to sort again
 | |
| 			if ( _sorted )
 | |
| 				return true;
 | |
| 				
 | |
| 			this.Sort( new PointPair.PointPairComparer( type ) );
 | |
| 			
 | |
| 			return false;
 | |
| 		}
 | |
| 		
 | |
| 		/// <summary>
 | |
| 		/// Set the X values for this <see cref="PointPairList"/> from the specified
 | |
| 		/// array of double values.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>
 | |
| 		/// If <see paramref="x"/> has more values than
 | |
| 		/// this list, then the extra values will be ignored.  If <see paramref="x"/>
 | |
| 		/// has less values, then the corresponding <see cref="PointPairList"/> values
 | |
| 		/// will not be changed.  That is, if the <see cref="PointPairList"/> has 20 values
 | |
| 		/// and <see paramref="x"/> has 15 values, then the first 15 values of the
 | |
| 		/// <see cref="PointPairList"/> will be changed, and the last 5 values will not be
 | |
| 		/// changed.
 | |
| 		/// </remarks>
 | |
| 		/// <param name="x">An array of double values that will replace the existing X
 | |
| 		/// values in the <see cref="PointPairList"/>.</param>
 | |
| 		public void SetX( double[] x )
 | |
| 		{
 | |
| 			for ( int i=0; i<x.Length; i++ )
 | |
| 			{
 | |
| 				if ( i < this.Count )
 | |
| 					this[i].X = x[i];
 | |
| 			}
 | |
| 				
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Set the Y values for this <see cref="PointPairList"/> from the specified
 | |
| 		/// array of double values.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>
 | |
| 		/// If <see paramref="y"/> has more values than
 | |
| 		/// this list, then the extra values will be ignored.  If <see paramref="y"/>
 | |
| 		/// has less values, then the corresponding <see cref="PointPairList"/> values
 | |
| 		/// will not be changed.  That is, if the <see cref="PointPairList"/> has 20 values
 | |
| 		/// and <see paramref="y"/> has 15 values, then the first 15 values of the
 | |
| 		/// <see cref="PointPairList"/> will be changed, and the last 5 values will not be
 | |
| 		/// changed.
 | |
| 		/// </remarks>
 | |
| 		/// <param name="y">An array of double values that will replace the existing Y
 | |
| 		/// values in the <see cref="PointPairList"/>.</param>
 | |
| 		public void SetY( double[] y )
 | |
| 		{
 | |
| 			for ( int i=0; i<y.Length; i++ )
 | |
| 			{
 | |
| 				if ( i < this.Count )
 | |
| 					this[i].Y = y[i];
 | |
| 			}
 | |
| 				
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Set the Z values for this <see cref="PointPairList"/> from the specified
 | |
| 		/// array of double values.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>
 | |
| 		/// If <see paramref="z"/> has more values than
 | |
| 		/// this list, then the extra values will be ignored.  If <see paramref="z"/>
 | |
| 		/// has less values, then the corresponding <see cref="PointPairList"/> values
 | |
| 		/// will not be changed.  That is, if the <see cref="PointPairList"/> has 20 values
 | |
| 		/// and <see paramref="z"/> has 15 values, then the first 15 values of the
 | |
| 		/// <see cref="PointPairList"/> will be changed, and the last 5 values will not be
 | |
| 		/// changed.
 | |
| 		/// </remarks>
 | |
| 		/// <param name="z">An array of double values that will replace the existing Z
 | |
| 		/// values in the <see cref="PointPairList"/>.</param>
 | |
| 		public void SetZ( double[] z )
 | |
| 		{
 | |
| 			for ( int i=0; i<z.Length; i++ )
 | |
| 			{
 | |
| 				if ( i < this.Count )
 | |
| 					this[i].Z = z[i];
 | |
| 			}
 | |
| 				
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add the Y values from the specified <see cref="PointPairList"/> object to this
 | |
| 		/// <see cref="PointPairList"/>.  If <see paramref="sumList"/> has more values than
 | |
| 		/// this list, then the extra values will be ignored.  If <see paramref="sumList"/>
 | |
| 		/// has less values, the missing values are assumed to be zero.
 | |
| 		/// </summary>
 | |
| 		/// <param name="sumList">A reference to the <see cref="PointPairList"/> object to
 | |
| 		/// be summed into the this <see cref="PointPairList"/>.</param>
 | |
| 		public void SumY( PointPairList sumList )
 | |
| 		{
 | |
| 			for ( int i=0; i<this.Count; i++ )
 | |
| 			{
 | |
| 				if ( i < sumList.Count )
 | |
| 					this[i].Y += sumList[i].Y;
 | |
| 			}
 | |
| 				
 | |
| 			//sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Add the X values from the specified <see cref="PointPairList"/> object to this
 | |
| 		/// <see cref="PointPairList"/>.  If <see paramref="sumList"/> has more values than
 | |
| 		/// this list, then the extra values will be ignored.  If <see paramref="sumList"/>
 | |
| 		/// has less values, the missing values are assumed to be zero.
 | |
| 		/// </summary>
 | |
| 		/// <param name="sumList">A reference to the <see cref="PointPairList"/> object to
 | |
| 		/// be summed into the this <see cref="PointPairList"/>.</param>
 | |
| 		public void SumX( PointPairList sumList )
 | |
| 		{
 | |
| 			for ( int i=0; i<this.Count; i++ )
 | |
| 			{
 | |
| 				if ( i < sumList.Count )
 | |
| 					this[i].X += sumList[i].X;
 | |
| 			}
 | |
| 				
 | |
| 			_sorted = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Linearly interpolate the data to find an arbitraty Y value that corresponds to the specified X value.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>
 | |
| 		/// This method uses linear interpolation with a binary search algorithm.  It therefore
 | |
| 		/// requires that the x data be monotonically increasing.  Missing values are not allowed.  This
 | |
| 		/// method will extrapolate outside the range of the PointPairList if necessary.
 | |
| 		/// </remarks>
 | |
| 		/// <param name="xTarget">The target X value on which to interpolate</param>
 | |
| 		/// <returns>The Y value that corresponds to the <see paramref="xTarget"/> value.</returns>
 | |
| 		public double InterpolateX( double xTarget )
 | |
| 		{
 | |
| 			int lo, mid, hi;
 | |
| 			if ( this.Count < 2 )
 | |
| 				throw new Exception( "Error: Not enough points in curve to interpolate" );
 | |
| 
 | |
| 			if ( xTarget <= this[0].X )
 | |
| 			{
 | |
| 				lo = 0;
 | |
| 				hi = 1;
 | |
| 			}
 | |
| 			else if ( xTarget >= this[this.Count-1].X )
 | |
| 			{
 | |
| 				lo = this.Count - 2;
 | |
| 				hi = this.Count - 1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				// if x is within the bounds of the x table, then do a binary search
 | |
| 				// in the x table to find table entries that bound the x value
 | |
| 				lo = 0;
 | |
| 				hi = this.Count - 1;
 | |
| 			    
 | |
| 				// limit to 1000 loops to avoid an infinite loop problem
 | |
| 				int j;
 | |
| 				for ( j=0; j<1000 && hi > lo + 1; j++ )
 | |
| 				{
 | |
| 					mid = ( hi + lo ) / 2;
 | |
| 					if ( xTarget > this[mid].X )
 | |
| 						lo = mid;
 | |
| 					else
 | |
| 						hi = mid;
 | |
| 				}
 | |
| 
 | |
| 				if ( j >= 1000 )
 | |
| 					throw new Exception( "Error: Infinite loop in interpolation" );
 | |
| 			}
 | |
| 
 | |
| 			return ( xTarget - this[lo].X ) / ( this[hi].X - this[lo].X ) *
 | |
| 					( this[hi].Y - this[lo].Y ) + this[lo].Y;
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Use Cardinal Splines to Interpolate the data to find an arbitraty Y value that corresponds to
 | |
| 		/// the specified X value.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>
 | |
| 		/// This method uses cardinal spline interpolation with a binary search algorithm.  It therefore
 | |
| 		/// requires that the x data be monotonically increasing.  Missing values are not allowed.  This
 | |
| 		/// method will not extrapolate outside the range of the PointPairList (it returns
 | |
| 		/// <see cref="PointPairBase.Missing"/> if extrapolation would be required).  WARNING: Cardinal
 | |
| 		/// spline interpolation can generate curves with non-unique X values for higher tension
 | |
| 		/// settings.  That is, there may be multiple X values for the same Y value.  This routine
 | |
| 		/// follows the path of the spline curve until it reaches the FIRST OCCURRENCE of the
 | |
| 		/// target X value.  It does not check to see if other solutions are possible.
 | |
| 		/// </remarks>
 | |
| 		/// <param name="xTarget">The target X value on which to interpolate</param>
 | |
| 		/// <param name="tension">The tension setting that controls the curvature of the spline fit.
 | |
| 		/// Typical values are between 0 and 1, where 0 is a linear fit, and 1 is lots of "roundness".
 | |
| 		/// Values greater than 1 may give odd results.
 | |
| 		/// </param>
 | |
| 		/// <returns>The Y value that corresponds to the <see paramref="xTarget"/> value.</returns>
 | |
| 		public double SplineInterpolateX( double xTarget, double tension )
 | |
| 		{
 | |
| 			// Scale the tension value to be compatible with the GDI+ values
 | |
| 			tension /= 3.0;
 | |
| 
 | |
| 			int lo, mid, hi;
 | |
| 			if ( this.Count < 2 )
 | |
| 				throw new Exception( "Error: Not enough points in curve to interpolate" );
 | |
| 
 | |
| 			// Extrapolation not allowed
 | |
| 			if ( xTarget <= this[0].X || xTarget >= this[this.Count-1].X )
 | |
| 				return PointPair.Missing;
 | |
| 			else
 | |
| 			{
 | |
| 				// if x is within the bounds of the x table, then do a binary search
 | |
| 				// in the x table to find table entries that bound the x value
 | |
| 				lo = 0;
 | |
| 				hi = this.Count - 1;
 | |
| 			    
 | |
| 				// limit to 1000 loops to avoid an infinite loop problem
 | |
| 				int j;
 | |
| 				for ( j=0; j<1000 && hi > lo + 1; j++ )
 | |
| 				{
 | |
| 					mid = ( hi + lo ) / 2;
 | |
| 					if ( xTarget > this[mid].X )
 | |
| 						lo = mid;
 | |
| 					else
 | |
| 						hi = mid;
 | |
| 				}
 | |
| 
 | |
| 				if ( j >= 1000 )
 | |
| 					throw new Exception( "Error: Infinite loop in interpolation" );
 | |
| 			}
 | |
| 
 | |
| 			// At this point, we know the two bounding points around our point of interest
 | |
| 			// We need the four points that surround our point
 | |
| 
 | |
| 			double X0, X1, X2, X3;
 | |
| 			double Y0, Y1, Y2, Y3;
 | |
| 			double B0, B1, B2, B3;
 | |
| 
 | |
| 			X1 = this[lo].X;
 | |
| 			X2 = this[hi].X;
 | |
| 			Y1 = this[lo].Y;
 | |
| 			Y2 = this[hi].Y;
 | |
| 
 | |
| 			// if we are at either the beginning of the table or the end, then make up a before
 | |
| 			// and/or after point to fill in the four points
 | |
| 			if ( lo == 0 )
 | |
| 			{
 | |
| 				X0 = X1 - ( X2 - X1 )/3;
 | |
| 				Y0 = Y1 - ( Y2 - Y1 )/3;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				X0 = this[lo-1].X;
 | |
| 				Y0 = this[lo-1].Y;
 | |
| 			}
 | |
| 
 | |
| 			if ( hi == this.Count - 1 )
 | |
| 			{
 | |
| 				X3 = X2 + ( X2 - X1 )/3;
 | |
| 				Y3 = Y2 + ( Y2 - Y1 )/3;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				X3 = this[hi+1].X;
 | |
| 				Y3 = this[hi+1].Y;
 | |
| 			}
 | |
| 
 | |
| 			double	newX, newY,
 | |
| 						lastX = X1,
 | |
| 						lastY = Y1;
 | |
| 
 | |
| 			// Do 100 steps to find the result
 | |
| 			for ( double t=0.01; t<=1; t+=0.01 )
 | |
| 			{
 | |
| 				B0 = (1 - t) * (1 - t) * (1 - t);
 | |
| 				B1 = 3.0 * t * (1 - t) * (1 - t);
 | |
| 				B2 = 3.0 * t * t * (1 - t);
 | |
| 				B3 = t * t * t;
 | |
| 
 | |
| 				newX = X1 * B0 + (X1 + (X2 - X0) * tension) * B1 +
 | |
| 						(X2 - (X3 - X1) * tension) * B2 + X2 * B3;
 | |
| 				newY = Y1 * B0 + (Y1 + (Y2 - Y0) * tension) * B1 +
 | |
| 						(Y2 - (Y3 - Y1) * tension) * B2 + Y2 * B3;
 | |
| 
 | |
| 				// We are looking for the first X that exceeds the target
 | |
| 				if ( newX >= xTarget )
 | |
| 				{
 | |
| 					// We now have two bounding X values around our target
 | |
| 					// use linear interpolation to minimize the discretization
 | |
| 					// error.
 | |
| 					return ( xTarget - lastX ) / ( newX - lastX ) *
 | |
| 							( newY - lastY ) + lastY;
 | |
| 				}
 | |
| 
 | |
| 				lastX = newX;
 | |
| 				lastY = newY;
 | |
| 			}
 | |
| 
 | |
| 			// This should never happen
 | |
| 			return Y2;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Linearly interpolate the data to find an arbitraty X value that corresponds to the specified Y value.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>
 | |
| 		/// This method uses linear interpolation with a binary search algorithm.  It therefore
 | |
| 		/// requires that the Y data be monotonically increasing.  Missing values are not allowed.  This
 | |
| 		/// method will extrapolate outside the range of the PointPairList if necessary.
 | |
| 		/// </remarks>
 | |
| 		/// <param name="yTarget">The target Y value on which to interpolate</param>
 | |
| 		/// <returns>The X value that corresponds to the <see paramref="yTarget"/> value.</returns>
 | |
| 		public double InterpolateY( double yTarget )
 | |
| 		{
 | |
| 			int lo, mid, hi;
 | |
| 			if ( this.Count < 2 )
 | |
| 				throw new Exception( "Error: Not enough points in curve to interpolate" );
 | |
| 
 | |
| 			if ( yTarget <= this[0].Y )
 | |
| 			{
 | |
| 				lo = 0;
 | |
| 				hi = 1;
 | |
| 			}
 | |
| 			else if ( yTarget >= this[this.Count-1].Y )
 | |
| 			{
 | |
| 				lo = this.Count - 2;
 | |
| 				hi = this.Count - 1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				// if y is within the bounds of the y table, then do a binary search
 | |
| 				// in the y table to find table entries that bound the y value
 | |
| 				lo = 0;
 | |
| 				hi = this.Count - 1;
 | |
| 			    
 | |
| 				// limit to 1000 loops to avoid an infinite loop problem
 | |
| 				int j;
 | |
| 				for ( j=0; j<1000 && hi > lo + 1; j++ )
 | |
| 				{
 | |
| 					mid = ( hi + lo ) / 2;
 | |
| 					if ( yTarget > this[mid].Y )
 | |
| 						lo = mid;
 | |
| 					else
 | |
| 						hi = mid;
 | |
| 				}
 | |
| 
 | |
| 				if ( j >= 1000 )
 | |
| 					throw new Exception( "Error: Infinite loop in interpolation" );
 | |
| 			}
 | |
| 
 | |
| 			return ( yTarget - this[lo].Y ) / ( this[hi].Y - this[lo].Y ) *
 | |
| 					( this[hi].X - this[lo].X ) + this[lo].X;
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Use linear regression to form a least squares fit of an existing
 | |
| 		/// <see cref="IPointList"/> instance.
 | |
| 		/// </summary>
 | |
| 		/// <remarks>The output <see cref="PointPairList" /> will cover the
 | |
| 		/// same X range of data as the original dataset.
 | |
| 		/// </remarks>
 | |
| 		/// <param name="points">An <see cref="IPointList" /> instance containing
 | |
| 		/// the data to be regressed.</param>
 | |
| 		/// <param name="pointCount">The number of desired points to be included
 | |
| 		/// in the resultant <see cref="PointPairList" />.
 | |
| 		/// </param>
 | |
| 		/// <returns>A new <see cref="PointPairList" /> containing the resultant
 | |
| 		/// data fit.
 | |
| 		/// </returns>
 | |
| 		public PointPairList LinearRegression( IPointList points, int pointCount )
 | |
| 		{
 | |
| 			double minX = double.MaxValue;
 | |
| 			double maxX = double.MinValue;
 | |
| 
 | |
| 			for ( int i=0; i<points.Count; i++ )
 | |
| 			{
 | |
| 				PointPair pt = points[i];
 | |
| 
 | |
| 				if ( !pt.IsInvalid )
 | |
| 				{
 | |
| 					minX = pt.X < minX ? pt.X : minX;
 | |
| 					maxX = pt.X > maxX ? pt.X : maxX;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return LinearRegression( points, pointCount, minX, maxX );
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Use linear regression to form a least squares fit of an existing
 | |
| 		/// <see cref="IPointList"/> instance.
 | |
| 		/// </summary>
 | |
| 		/// <param name="points">An <see cref="IPointList" /> instance containing
 | |
| 		/// the data to be regressed.</param>
 | |
| 		/// <param name="pointCount">The number of desired points to be included
 | |
| 		/// in the resultant <see cref="PointPairList" />.
 | |
| 		/// </param>
 | |
| 		/// <param name="minX">The minimum X value of the resultant
 | |
| 		/// <see cref="PointPairList" />.</param>
 | |
| 		/// <param name="maxX">The maximum X value of the resultant
 | |
| 		/// <see cref="PointPairList" />.</param>
 | |
| 		/// <returns>A new <see cref="PointPairList" /> containing the resultant
 | |
| 		/// data fit.
 | |
| 		/// </returns>
 | |
| 		/// <author> Brian Chappell - lazarusds
 | |
| 		///          modified by John Champion</author>
 | |
| 		public PointPairList LinearRegression( IPointList points, int pointCount,
 | |
| 			double minX, double maxX )
 | |
| 		{
 | |
| 			double x = 0, y = 0, xx = 0, xy = 0, count = 0;
 | |
| 			for ( int i = 0; i < points.Count; i++ )
 | |
| 			{
 | |
| 				PointPair pt = points[i];
 | |
| 				if ( !pt.IsInvalid )
 | |
| 				{
 | |
| 					x += points[i].X;
 | |
| 					y += points[i].Y;
 | |
| 					xx += points[i].X * points[i].X;
 | |
| 					xy += points[i].X * points[i].Y;
 | |
| 					count++;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if ( count < 2 || maxX - minX < 1e-20 )
 | |
| 				return null;
 | |
| 
 | |
| 			double slope = ( count * xy - x * y ) / ( count * xx - x * x );
 | |
| 			double intercept = ( y - slope * x ) / count;
 | |
| 
 | |
| 			PointPairList newPoints = new PointPairList();
 | |
| 			double stepSize = ( maxX - minX ) / pointCount;
 | |
| 			double value = minX;
 | |
| 			for ( int i = 0; i < pointCount; i++ )
 | |
| 			{
 | |
| 				newPoints.Add( new PointPair( value, value * slope + intercept ) );
 | |
| 				value += stepSize;
 | |
| 			}
 | |
| 
 | |
| 			return newPoints;
 | |
| 		} 
 | |
| 
 | |
| 
 | |
| 	#endregion
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 |