//============================================================================
//ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C#
//Copyright ?2006  John Champion
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//=============================================================================
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace DrawGraph
{
	/// 
	/// A simple storage struct to maintain an individual sampling of data.  This only
	/// contains two data values in order to reduce to memory load for large datasets.
	/// (e.g., no Tag or Z property)
	/// 
	public struct DataPoint
	{
		/// 
		/// The X value for the point, stored as a double type.
		/// 
		public double X;
		/// 
		/// The Y value for the point, stored as a double type.
		/// 
		public double Y;
	}
	/// 
	/// A collection class to maintain a set of samples.
	/// 
	/// This type, intended for very
	/// large datasets, will reduce the number of points displayed by eliminating
	/// individual points that overlay (at the same pixel location) on the graph.
	/// Note that this type probably does not make sense for line plots, but is intended
	/// primarily for scatter plots.
	/// 
	/// 
	///  John Champion 
	///  $Revision: 3.5 $ $Date: 2007/06/02 06:56:03 $ 
	[Serializable]
	public class NoDupePointList : List, IPointList, IPointListEdit
	{
		/// 
		/// Protected field that stores a value indicating whether or not the data have been filtered.
		/// If the data have not been filtered, then  will be equal to
		/// .  Use the public property  to
		/// access this value.
		/// 
		protected bool _isFiltered;
		/// 
		/// Protected field that stores the number of data points after filtering (e.g.,
		///  has been called).  The  property
		/// returns the total count for an unfiltered dataset, or 
		/// for a dataset that has been filtered.
		/// 
		protected int _filteredCount;
		/// 
		/// Protected array of indices for all the points that are currently visible.  This only
		/// applies if  is true.
		/// 
		protected int[] _visibleIndicies;
		/// 
		/// Protected field that stores a value that determines how close a point must be to a prior
		/// neighbor in order to be filtered out.  Use the public property 
		/// to access this value.
		/// 
		protected int _filterMode;
		/// 
		/// Gets or sets a value that determines how close a point must be to a prior
		/// neighbor in order to be filtered out.
		/// 
		/// 
		/// A value of 0 indicates that subsequent
		/// points must coincide exactly at the same pixel location.  A value of 1 or more
		/// indicates that number of pixels distance from a prior point that will cause
		/// a new point to be filtered out.  For example, a value of 2 means that, once
		/// a particular pixel location is taken, any subsequent point that lies within 2
		/// pixels of that location will be filtered out.
		/// 
		public int FilterMode
		{
			get { return _filterMode; }
			set { _filterMode = value; }
		}
		/// 
		/// Gets a value indicating whether or not the data have been filtered.  If the data
		/// have not been filtered, then  will be equal to
		/// .
		/// 
		public bool IsFiltered
		{
			get { return _isFiltered; }
		}
		/// 
		/// Indexer: get the DataPoint instance at the specified ordinal position in the list
		/// 
		/// 
		/// This method will throw an exception if the index is out of range.  This can happen
		/// if the index is less than the number of filtered values, or if data points are
		/// removed from a filtered dataset with updating the filter (by calling
		/// ).
		/// 
		/// The ordinal position in the list of points
		/// Returns a  instance.  The 
		/// and  properties will be defaulted to
		///  and null, respectively.
		/// 
		public new PointPair this[int index]
		{
			get
			{
				int j = index;
				if ( _isFiltered )
					j = _visibleIndicies[index];
				DataPoint dp = base[j];
				PointPair pt = new PointPair( dp.X, dp.Y );
				return pt;
			}
			set
			{
				int j = index;
				if ( _isFiltered )
					j = _visibleIndicies[index];
				DataPoint dp;
				dp.X = value.X;
				dp.Y = value.Y;
				base[j] = dp;
			}
		}
		/// 
		/// Gets the number of active samples in the collection.  This is the number of
		/// samples that are non-duplicates.  See the  property
		/// to get the total number of samples in the list.
		/// 
		public new int Count
		{
			get
			{
				if ( !_isFiltered )
					return base.Count;
				else
					return _filteredCount;
			}
		}
		/// 
		/// Gets the total number of samples in the collection.  See the 
		/// property to get the number of active (non-duplicate) samples in the list.
		/// 
		public int TotalCount
		{
			get { return base.Count; }
		}
		/// 
		/// Append a data point to the collection
		/// 
		/// The  value to append
		public void Add( PointPair pt )
		{
			DataPoint dp = new DataPoint();
			dp.X = pt.X;
			dp.Y = pt.Y;
			Add( dp );
		}
		/// 
		/// Append a point to the collection
		/// 
		/// The x value of the point to append
		/// The y value of the point to append
		public void Add( double x, double y )
		{
			DataPoint dp = new DataPoint();
			dp.X = x;
			dp.Y = y;
			Add( dp );
		}
		// generic Clone: just call the typesafe version
		object ICloneable.Clone()
		{
			return this.Clone();
		}
		/// 
		/// typesafe clone method
		/// 
		/// A new cloned NoDupePointList.  This returns a copy of the structure,
		/// but it does not duplicate the data (it just keeps a reference to the original)
		/// 
		public NoDupePointList Clone()
		{
			return new NoDupePointList( this );
		}
		/// 
		/// default constructor
		/// 
		public NoDupePointList()
		{
			_isFiltered = false;
			_filteredCount = 0;
			_visibleIndicies = null;
			_filterMode = 0;
		}
		/// 
		/// copy constructor -- this returns a copy of the structure,
		/// but it does not duplicate the data (it just keeps a reference to the original)
		/// 
		/// The NoDupePointList to be copied
		public NoDupePointList( NoDupePointList rhs )
		{
			int count = rhs.TotalCount;
			for ( int i = 0; i < count; i++ )
				Add( rhs.GetDataPointAt( i ) );
			_filteredCount = rhs._filteredCount;
			_isFiltered = rhs._isFiltered;
			_filterMode = rhs._filterMode;
			if ( rhs._visibleIndicies != null )
				_visibleIndicies = (int[]) rhs._visibleIndicies.Clone();
			else
				_visibleIndicies = null;
		}
		/// 
		/// Protected method to access the internal DataPoint collection, without any
		/// translation to a PointPair.
		/// 
		/// The ordinal position of the DataPoint of interest
		protected DataPoint GetDataPointAt( int index )
		{
			return base[index];
		}
		/// 
		/// Clears any filtering previously done by a call to .
		/// After calling this method, all data points will be visible, and
		///  will be equal to .
		/// 
		public void ClearFilter()
		{
			_isFiltered = false;
			_filteredCount = 0;
		}
		/// 
		/// Go through the collection, and hide (filter out) any points that fall on the
		/// same pixel location as a previously included point.
		/// 
		/// 
		/// This method does not delete any points, it just temporarily hides them until
		/// the next call to  or .
		/// You should call  once your collection of points has
		/// been constructed.  You may need to call  again if
		/// you add points, or if the chart rect changes size (by resizing, printing,
		/// image save, etc.), or if the scale range changes.
		/// You must call  before calling
		/// this method so that the GraphPane.Chart.Rect
		/// and the scale ranges are valid.  This method is not valid for
		/// ordinal axes (but ordinal axes don't make sense for very large datasets
		/// anyway).
		/// 
		/// The  into which the data
		/// will be plotted. 
		/// The  class to be used in the Y direction
		/// for plotting these data.  This can be a  or a 
		/// , and can be a primary or secondary axis (if multiple Y or Y2
		/// axes are being used).
		/// 
		/// The  class to be used in the X direction
		/// for plotting these data.  This can be an  or a 
		/// .
		/// 
		public void FilterData( GraphPane pane, Axis xAxis, Axis yAxis )
		{
			if ( _visibleIndicies == null || _visibleIndicies.Length < base.Count )
				_visibleIndicies = new int[base.Count];
			_filteredCount = 0;
			_isFiltered = true;
			int width = (int)pane.Chart.Rect.Width;
			int height = (int)pane.Chart.Rect.Height;
			if ( width <= 0 || height <= 0 )
				throw new IndexOutOfRangeException( "Error in FilterData: Chart rect not valid" );
			bool[,] usedArray = new bool[width, height];
			for ( int i = 0; i < width; i++ )
				for ( int j = 0; j < height; j++ )
					usedArray[i, j] = false;
			xAxis.Scale.SetupScaleData( pane, xAxis );
			yAxis.Scale.SetupScaleData( pane, yAxis );
			int n = _filterMode < 0 ? 0 : _filterMode;
			int left = (int)pane.Chart.Rect.Left;
			int top = (int)pane.Chart.Rect.Top;
			for ( int i=0; i= 0 && x < width && y >= 0 && y < height )
				{
					bool used = false;
					if ( n <= 0 )
						used = usedArray[x, y];
					else
					{
						for ( int ix = x - n; ix <= x + n; ix++ )
							for ( int iy = y - n; iy <= y + n; iy++ )
								used |= ( ix >= 0 && ix < width && iy >= 0 && iy < height && usedArray[ix, iy] );
					}
					if ( !used )
					{
						usedArray[x, y] = true;
						_visibleIndicies[_filteredCount] = i;
						_filteredCount++;
					}
				}
			}
		}
	}
}