//============================================================================ //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++; } } } } } }