Source code: graph/DataSet.java
1 package graph;
2
3 import java.awt.*;
4 import java.applet.*;
5 import java.util.*;
6 import java.lang.*;
7
8
9 /*
10 **************************************************************************
11 **
12 ** Class DataSet
13 **
14 **************************************************************************
15 ** Copyright (C) 1995, 1996 Leigh Brookshaw
16 **
17 ** This program is free software; you can redistribute it and/or modify
18 ** it under the terms of the GNU General Public License as published by
19 ** the Free Software Foundation; either version 2 of the License, or
20 ** (at your option) any later version.
21 **
22 ** This program is distributed in the hope that it will be useful,
23 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 ** GNU General Public License for more details.
26 **
27 ** You should have received a copy of the GNU General Public License
28 ** along with this program; if not, write to the Free Software
29 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 **************************************************************************
31 ** Modified by Jim Cochrane, last changed in May, 2001
32 **************************************************************************
33 **
34 ** This class is designed to be used in conjunction with
35 ** the Graph class and Axis class for plotting 2D graphs.
36 **
37 *************************************************************************/
38
39
40 /**
41 * This class is designed to hold the data to be plotted.
42 * It is to be used in conjunction with the Graph class and Axis
43 * class for plotting 2D graphs.
44 *
45 * @version $Revision: 2.31 $, $Date: 2001/05/29 09:36:38 $
46 * @author Leigh Brookshaw
47 */
48 public class DataSet {
49
50 public void set_drawer (BasicDrawer d) {
51 _drawer = d;
52 }
53
54 public void set_dates (String d[]) {
55 dates = d;
56 _drawer.set_dates(d);
57 }
58
59 public void set_times (String t[]) {
60 times = t;
61 _drawer.set_times(t);
62 }
63
64 public BasicDrawer drawer() { return _drawer; }
65
66 // Do dates need to be drawn?
67 public boolean dates_needed() { return _dates_needed; }
68
69 public void set_dates_needed(boolean b) { _dates_needed = b; }
70
71 // Number of records in this data set
72 public int count() {
73 return tuple_count;
74 }
75
76 /*
77 ***********************
78 ** Variables
79 **********************/
80
81 /**
82 * The Graphics canvas that is driving the whole show.
83 * @see graph.Graph
84 */
85 private Graph g2d_;
86 /**
87 * The color of the straight line segments
88 */
89 private Color linecolor = null;
90 /**
91 * The index of the marker to use at the data points.
92 * @see graph.Markers
93 */
94 private int marker = 0;
95 /**
96 * The marker color
97 */
98 private Color markercolor = null;
99 /**
100 * The scaling factor for the marker. Default value is 1.
101 */
102 private double markerscale = 1.0;
103 /**
104 * The Axis object the X data is attached to. From the Axis object
105 * the scaling for the data can be derived.
106 * @see graph.Axis
107 */
108 private Axis xaxis_;
109 /**
110 * The Axis object the Y data is attached to.
111 * @see graph.Axis
112 */
113 private Axis yaxis_;
114 /**
115 * The current plottable X maximum of the data.
116 * This can be very different from
117 * true data X maximum. The data is clipped when plotted.
118 */
119 private double xmax;
120 /**
121 * The current plottable X minimum of the data.
122 * This can be very different from
123 * true data X minimum. The data is clipped when plotted.
124 */
125 private double xmin;
126 /**
127 * The current plottable Y maximum of the data.
128 * This can be very different from
129 * true data Y maximum. The data is clipped when plotted.
130 */
131 private double ymax;
132 /**
133 * The current plottable Y minimum of the data.
134 * This can be very different from
135 * true data Y minimum. The data is clipped when plotted.
136 */
137 private double ymin;
138 /**
139 * Boolean to control clipping of the data window.
140 * Default value is <em>true</em>, clip the data window.
141 */
142 private boolean clipping = false;
143
144 /*
145 *********************
146 ** Constructors
147 ********************/
148
149 /**
150 * Instantiate an empty data set.
151 * @precondition
152 * d != null
153 * @postcondition
154 * dates_needed() && drawer() == drawer
155 */
156 public DataSet(BasicDrawer d) {
157 if (d == null) {
158 throw new Error("DataSet constructor: precondition violated");
159 }
160 _drawer = d;
161 data = null;
162 date_drawer = new DateDrawer(d);
163 time_drawer = new TimeDrawer(d);
164 _dates_needed = true;
165 tuple_count = 0;
166 }
167
168 /**
169 * Instantiate a DataSet with the parsed data.
170 * `d' contains a set of tuples flattened out into array elements -
171 * for example, for a 4-field tuple, d[0..3] are the fields of the
172 # first tuple, d[4..7] are the fields of the second tuple, etc.
173 * The integer n is the number of data Points. This means that the
174 * length of the data array is t*n, where t is the number of fields
175 * in a tuple.
176 * Note: `d' is expected to take y-position (vertical) data only;
177 * x-positions will be calculated based on the position of each
178 * tuple in `d' - from 1 to d.length.
179 * @param d Array containing the (y1,y2,...) data tuples.
180 * @param n Number of tuples in the array.
181 * @param drawer object used to draw the data.
182 * @precondition
183 * d != null && d.length > 0 && n > 0 && drawer != null
184 * @postcondition
185 * dates_needed() && drawer() == drawer
186 */
187 public DataSet(double d[], int n, BasicDrawer drawer) throws Error {
188 if ( d == null || d.length == 0 || n <= 0 || drawer == null ) {
189 throw new Error("DataSet constructor: precondition violated");
190 }
191 int i;
192 _drawer = drawer;
193 date_drawer = new DateDrawer(drawer);
194 time_drawer = new TimeDrawer(drawer);
195 data = d;
196
197 tuple_count = n;
198 _dates_needed = true;
199 }
200
201 /*******************
202 ** Public Methods
203 ******************/
204
205 // x axis
206 public Axis xaxis() { return xaxis_; }
207
208 // y axis
209 public Axis yaxis() { return yaxis_; }
210
211 // Color data is to be drawn in - can be null
212 public Color color() { return color_; }
213
214 // Set the x axis to `a'.
215 public void set_xaxis(Axis a) { xaxis_ = a; }
216
217 // Set the y axis to `a'.
218 public void set_yaxis(Axis a) { yaxis_ = a; }
219
220 // Set the color the data is to be drawn in - can be null
221 public void set_color(Color c) { color_ = c; }
222
223 // Add y1, y2 values for a horizontal line.
224 public void add_hline(DoublePair p) {
225 if (hline_data == null) hline_data = new Vector();
226 hline_data.addElement(p);
227 }
228
229 // Add x1, x2 values for a vertical line.
230 public void add_vline(DoublePair p) {
231 if (vline_data == null) vline_data = new Vector();
232 vline_data.addElement(p);
233 }
234
235 public void set_reference_values_needed (boolean b) {
236 _drawer.set_reference_values_needed (b);
237 }
238
239 /**
240 * Draw the straight line segments and/or the markers at the
241 * data points.
242 * If this data has been attached to an Axis then scale the data
243 * based on the axis maximum/minimum otherwise scale using
244 * the data's maximum/minimum
245 * @param g Graphics state
246 * @param bounds The data window to draw into
247 */
248 public void draw_data(Graphics g, Rectangle bounds) {
249 boolean restore_bounds = false;
250 if (! range_set) range();
251 if ( linecolor != null) g.setColor(linecolor);
252 _drawer.set_data(data);
253 _drawer.set_xaxis(xaxis_);
254 _drawer.set_yaxis(yaxis_);
255 _drawer.set_maxes(xmax, ymax, xmin, ymin);
256 _drawer.set_ranges(xrange, yrange);
257 _drawer.set_clipping(clipping);
258 _drawer.draw_data(g, bounds, hline_data, vline_data, color_);
259 }
260
261 /**
262 * return the data X maximum.
263 */
264 public double getXmax() { return dxmax; }
265 /**
266 * return the data X minimum.
267 */
268 public double getXmin() { return dxmin; }
269 /**
270 * return the data Y maximum.
271 */
272 public double getYmax() { return dymax; }
273 /**
274 * return the data Y minimum.
275 */
276 public double getYmin() { return dymin; }
277
278 /**
279 * Return the number of data points in the DataSet
280 * @return number of (x,y) points.
281 */
282 public int dataPoints() { return length()/stride(); }
283
284 /**
285 * get the data point at the parsed index. The first (x,y) pair
286 * is at index 0.
287 * @param index Data point index
288 * @return array containing the (x,y) pair.
289 */
290 public double[] getPoint(int index) {
291 int strd = stride();
292 double point[] = new double[strd];
293 int i = index*strd;
294 if( index < 0 || i > length()-strd ) return null;
295
296 for(int j=0; j<strd; j++) point[j] = data[i+j];
297
298 return point;
299 }
300
301 /**
302 * Return the data point that is closest to the parsed (x,y) position
303 * @param x
304 * @param y (x,y) position in data space.
305 * @return array containing the closest data point.
306 */
307 public double[] getClosestPoint(double x, double y) {
308 double point[] = {0.0, 0.0, 0.0};
309 int i;
310 double xdiff, ydiff, dist2;
311 int strd = stride();
312
313 xdiff = data[0] - x;
314 ydiff = data[1] - y;
315 point[0] = data[0];
316 point[1] = data[1];
317 point[2] = xdiff*xdiff + ydiff*ydiff;
318
319 for(i=strd; i<length()-1; i+=strd) {
320 xdiff = data[i ] - x;
321 ydiff = data[i+1] - y;
322 dist2 = xdiff*xdiff + ydiff*ydiff;
323 if(dist2 < point[2]) {
324 point[0] = data[i ];
325 point[1] = data[i+1];
326 point[2] = dist2;
327 }
328 }
329
330 return point;
331 }
332
333 // set g2d to `b'.
334 protected void set_g2d(Graph g) { g2d_ = g; }
335
336 // The `time_drawer', if there are times; otherwise the `date_drawer',
337 // if there are dates; otherwise null
338 protected TemporalDrawer temporal_drawer() {
339 TemporalDrawer result = null;
340
341 if (times != null) result = time_drawer;
342 else if (dates != null) result = date_drawer;
343
344 return result;
345 }
346
347 /**
348 * Draw times, if they exist; otherwise date dates, if they exist.
349 * Precondition: temporal_drawer() != null implies
350 * temporal_drawer().main_data_processed()
351 * @param g Graphics context
352 * @param w Data Window
353 */
354 protected void draw_dates(Graphics g, Rectangle w) {
355 TemporalDrawer drawer = temporal_drawer();
356 if (drawer != null && drawer.main_data_processed()) {
357 drawer.set_xaxis(xaxis_);
358 drawer.set_yaxis(yaxis_);
359 drawer.set_maxes(xmax, ymax, xmin, ymin);
360 drawer.set_ranges(xrange, yrange);
361 drawer.set_clipping(clipping);
362 drawer.draw_data(g, w);
363 }
364 }
365
366 /**
367 * Calculate the range of the data. This modifies dxmin,dxmax,dymin,dymax
368 * and xmin,xmax,ymin,ymax
369 * Precondition: tuple_count has been set
370 */
371 protected void range() {
372 int i;
373 int lnth = length();
374 int stride = stride();
375
376 if (lnth >= stride ) {
377 dymax = data[0];
378 dymin = dymax;
379 // The range for x follows the data index - starts at 1
380 // and ends at data.length.
381 dxmax = tuple_count;
382 dxmin = 1;
383 } else {
384 dxmin = 0.0;
385 dxmax = 0.0;
386 dymin = 0.0;
387 dymax = 0.0;
388 }
389
390 // `data' holds only y values - find the largest and smallest.
391 for (i = 0; i < lnth; ++i) {
392 if( dymax < data[i] ) { dymax = data[i]; }
393 else if( dymin > data[i] ) { dymin = data[i]; }
394 }
395 if ( yaxis_ == null) {
396 ymin = dymin;
397 ymax = dymax;
398 }
399 if ( xaxis_ == null) {
400 xmin = dxmin;
401 xmax = dxmax;
402 }
403 range_set = true;
404 }
405
406 /*
407 *********************
408 ** Protected Variables
409 **********************/
410
411 // Main data
412 private double[] data;
413
414 // Date data
415 protected String[] dates;
416
417 // Time data
418 protected String[] times;
419
420 // Color data is to be drawn in - can be null
421 private Color color_;
422
423 /**
424 * Drawer of price bars - e.g., tic bars or candles
425 */
426 private BasicDrawer _drawer;
427
428 /**
429 * Drawer of dates
430 */
431 protected DateDrawer date_drawer;
432
433 /**
434 * Drawer of times
435 */
436 private TimeDrawer time_drawer;
437
438 /**
439 * The data X maximum.
440 * Once the data is loaded this will never change.
441 */
442 private double dxmax;
443
444 /**
445 * The data X minimum.
446 * Once the data is loaded this will never change.
447 */
448 private double dxmin;
449
450 /**
451 * The data Y maximum.
452 * Once the data is loaded this will never change.
453 */
454 private double dymax;
455
456 /**
457 * The data Y minimum.
458 * Once the data is loaded this will never change.
459 */
460 private double dymin;
461
462 // Horizontal, vertical line data
463 private Vector hline_data;
464 private Vector vline_data;
465
466 /**
467 * The X range of the clipped data
468 */
469 private double xrange;
470
471 /**
472 * The Y range of the clipped data
473 */
474 private double yrange;
475
476 /**
477 * The amount to increment the data array when the append method is being
478 * used.
479 */
480 private int increment = 100;
481
482 // Has range() been called to set the range?
483 private boolean range_set = false;
484
485 // Number of tuples in the data
486 private int tuple_count;
487
488 /**
489 * Number of components in a data tuple - for example, 2 (x, y) for
490 * a simple point
491 * @precondition
492 * _drawer != null
493 */
494 protected int stride() { return _drawer.drawing_stride(); }
495
496 protected int length() {
497 int result = 0;
498 if (data != null) result = data.length;
499 return result;
500 }
501
502 private boolean _dates_needed;
503 }