NoDupePointList.cs
11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
//============================================================================
//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 ZedGraph
{
/// <summary>
/// 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)
/// </summary>
public struct DataPoint
{
/// <summary>
/// The X value for the point, stored as a double type.
/// </summary>
public double X;
/// <summary>
/// The Y value for the point, stored as a double type.
/// </summary>
public double Y;
}
/// <summary>
/// A collection class to maintain a set of samples.
/// </summary>
/// <remarks>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.
/// </remarks>
///
/// <author> John Champion </author>
/// <version> $Revision: 3.5 $ $Date: 2007/06/02 06:56:03 $ </version>
[Serializable]
public class NoDupePointList : List<DataPoint>, IPointList, IPointListEdit
{
/// <summary>
/// Protected field that stores a value indicating whether or not the data have been filtered.
/// If the data have not been filtered, then <see cref="Count" /> will be equal to
/// <see cref="TotalCount" />. Use the public property <see cref="IsFiltered" /> to
/// access this value.
/// </summary>
protected bool _isFiltered;
/// <summary>
/// Protected field that stores the number of data points after filtering (e.g.,
/// <see cref="FilterData" /> has been called). The <see cref="Count" /> property
/// returns the total count for an unfiltered dataset, or <see cref="_filteredCount" />
/// for a dataset that has been filtered.
/// </summary>
protected int _filteredCount;
/// <summary>
/// Protected array of indices for all the points that are currently visible. This only
/// applies if <see cref="IsFiltered" /> is true.
/// </summary>
protected int[] _visibleIndicies;
/// <summary>
/// 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 <see cref="FilterMode" />
/// to access this value.
/// </summary>
protected int _filterMode;
/// <summary>
/// Gets or sets a value that determines how close a point must be to a prior
/// neighbor in order to be filtered out.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public int FilterMode
{
get { return _filterMode; }
set { _filterMode = value; }
}
/// <summary>
/// Gets a value indicating whether or not the data have been filtered. If the data
/// have not been filtered, then <see cref="Count" /> will be equal to
/// <see cref="TotalCount" />.
/// </summary>
public bool IsFiltered
{
get { return _isFiltered; }
}
/// <summary>
/// Indexer: get the DataPoint instance at the specified ordinal position in the list
/// </summary>
/// <remarks>
/// 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
/// <see cref="FilterData" />).
/// </remarks>
/// <param name="index">The ordinal position in the list of points</param>
/// <returns>Returns a <see cref="PointPair" /> instance. The <see cref="PointPair.Z" />
/// and <see cref="PointPair.Tag" /> properties will be defaulted to
/// <see cref="PointPairBase.Missing" /> and null, respectively.
/// </returns>
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;
}
}
/// <summary>
/// Gets the number of active samples in the collection. This is the number of
/// samples that are non-duplicates. See the <see cref="TotalCount" /> property
/// to get the total number of samples in the list.
/// </summary>
public new int Count
{
get
{
if ( !_isFiltered )
return base.Count;
else
return _filteredCount;
}
}
/// <summary>
/// Gets the total number of samples in the collection. See the <see cref="Count" />
/// property to get the number of active (non-duplicate) samples in the list.
/// </summary>
public int TotalCount
{
get { return base.Count; }
}
/// <summary>
/// Append a data point to the collection
/// </summary>
/// <param name="pt">The <see cref="PointPair" /> value to append</param>
public void Add( PointPair pt )
{
DataPoint dp = new DataPoint();
dp.X = pt.X;
dp.Y = pt.Y;
Add( dp );
}
/// <summary>
/// Append a point to the collection
/// </summary>
/// <param name="x">The x value of the point to append</param>
/// <param name="y">The y value of the point to append</param>
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();
}
/// <summary>
/// typesafe clone method
/// </summary>
/// <returns>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)
/// </returns>
public NoDupePointList Clone()
{
return new NoDupePointList( this );
}
/// <summary>
/// default constructor
/// </summary>
public NoDupePointList()
{
_isFiltered = false;
_filteredCount = 0;
_visibleIndicies = null;
_filterMode = 0;
}
/// <summary>
/// copy constructor -- this returns a copy of the structure,
/// but it does not duplicate the data (it just keeps a reference to the original)
/// </summary>
/// <param name="rhs">The NoDupePointList to be copied</param>
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;
}
/// <summary>
/// Protected method to access the internal DataPoint collection, without any
/// translation to a PointPair.
/// </summary>
/// <param name="index">The ordinal position of the DataPoint of interest</param>
protected DataPoint GetDataPointAt( int index )
{
return base[index];
}
/// <summary>
/// Clears any filtering previously done by a call to <see cref="FilterData" />.
/// After calling this method, all data points will be visible, and
/// <see cref="Count" /> will be equal to <see cref="TotalCount" />.
/// </summary>
public void ClearFilter()
{
_isFiltered = false;
_filteredCount = 0;
}
/// <summary>
/// Go through the collection, and hide (filter out) any points that fall on the
/// same pixel location as a previously included point.
/// </summary>
/// <remarks>
/// This method does not delete any points, it just temporarily hides them until
/// the next call to <see cref="FilterData" /> or <see cref="ClearFilter" />.
/// You should call <see cref="FilterData" /> once your collection of points has
/// been constructed. You may need to call <see cref="FilterData" /> 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 <see cref="GraphPane.AxisChange()" /> before calling
/// this method so that the <see cref="Chart.Rect">GraphPane.Chart.Rect</see>
/// 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).
/// </remarks>
/// <param name="pane">The <see cref="GraphPane" /> into which the data
/// will be plotted. </param>
/// <param name="yAxis">The <see cref="Axis" /> class to be used in the Y direction
/// for plotting these data. This can be a <see cref="YAxis" /> or a
/// <see cref="Y2Axis" />, and can be a primary or secondary axis (if multiple Y or Y2
/// axes are being used).
/// </param>
/// <param name="xAxis">The <see cref="Axis" /> class to be used in the X direction
/// for plotting these data. This can be an <see cref="XAxis" /> or a
/// <see cref="X2Axis" />.
/// </param>
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<base.Count; i++ )
{
DataPoint dp = base[i];
int x = (int)( xAxis.Scale.Transform( dp.X ) + 0.5 ) - left;
int y = (int)( yAxis.Scale.Transform( dp.Y ) + 0.5 ) - top;
if ( x >= 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++;
}
}
}
}
}
}