001: /**
002: * Chart2D, a java library for drawing two dimensional charts.
003: * Copyright (C) 2001 Jason J. Simas
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * The author of this library may be contacted at:
019: * E-mail: jjsimas@users.sourceforge.net
020: * Street Address: J J Simas, 887 Tico Road, Ojai, CA 93023-3555 USA
021: */package net.sourceforge.chart2d;
022:
023: import java.util.Vector;
024: import java.awt.geom.*;
025:
026: /**
027: * The container for the data values to chart.
028: * A dataset is like a third order array (ex. float[][][]). The first order is
029: * the "sets" order. A set contains data divided by category. The "sets" refer
030: * to the objects described by the legend labels, if a legend exists. The
031: * second order is the "cats" or categories order. Data within a set can be
032: * divided by category. A category can have multiple data items. If using a
033: * graph chart, the categories are described by the labels-axis labels. For pie
034: * charts, there is only one category. The third order is the "items" order.
035: * The "items" order are the values for a particular category and set.<br>
036: * For example, if we had data for the years 1999 and 2000 of how many units were
037: * sold on each day during that time, then one way to chart this would be using
038: * a graph chart and making the years correspond to the "sets" order, "months",
039: * correspond to the "cats" order, and the number of units sold per day
040: * correspond to the "items" order.<br>
041: * There are two important rules to
042: * mention about this. For each set, the number of cats must be the same. For
043: * each cat, the number of items must be the same. In our example, we would
044: * probably want to choose the number of items per category to be 30,
045: * corresponding the average number of days per month. For months with less
046: * than thirty days, we would populate the unfilled days with the average of the
047: * filled days or simply carry the last value of the filled days into the
048: * unfilled days. For months with more than thirty days, we would average the
049: * last two days, and use that value as the value of the last day.<br>
050: * If passing to a PieChart2DProperties, the value used for each pie sector is
051: * the sum of the values in each set of data.
052: * Pass this to any number of PieChart2D or GraphChart2D objects.
053: */
054: public final class Dataset {
055:
056: private Vector setVector;
057: private float greatest;
058: private float least;
059:
060: private boolean needsUpdate = true;
061: private Vector needsUpdateVector = new Vector(5, 5);
062: private Vector chart2DVector = new Vector(5, 5);
063:
064: /**
065: * Creates a dataset with 0 sets, 0 categories, and 0 items.
066: * Use the add (int, int, int, float) method to add data items.
067: */
068: public Dataset() {
069:
070: needsUpdate = true;
071: setDatasetToDefaults();
072: }
073:
074: /**
075: * Creates a dataset with the specified number of sets, categories per set,
076: * and items per set and per category.
077: * All internal objects are created at once.
078: * Use the set (int, int, int, float) method to add data items.
079: * @param sets The number of sets.
080: * @param cats The number of categories per set.
081: * @param items The number of items per category and per set.
082: */
083: public Dataset(int sets, int cats, int items) {
084:
085: needsUpdate = true;
086: constructor(sets, cats, items);
087: }
088:
089: /**
090: * Creates a dataset that is a copy of another dataset.
091: * Copying is a deep copy.
092: * @param dataset The dataset to copy.
093: */
094: public Dataset(Dataset dataset) {
095:
096: needsUpdate = true;
097: set(dataset);
098: }
099:
100: /**
101: * Sets the dataset to its default state.
102: * It's default state is a dataset with no sets, no cats, and no items.
103: */
104: public final void setDatasetToDefaults() {
105:
106: needsUpdate = true;
107: constructor(0, 0, 0);
108: }
109:
110: /**
111: * Sets the value for the variable in the set specified by set, the
112: * category specified by cat within the set, and the item specified by item
113: * within the category.
114: * If internal resources have not yet been allocated for this item, a null pointer will occurr.
115: * @param set The specified set of the variable.
116: * @param cat The specified cat of the variable.
117: * @param item The specified item of the variable.
118: * @param value The specified value of the variable.
119: */
120: public final void set(int set, int cat, int item, float value) {
121:
122: needsUpdate = true;
123: ((Vector) ((Vector) setVector.get(set)).get(cat)).set(item,
124: new Float(value));
125: }
126:
127: /**
128: * Sets all the values of this dataset from another Dataset.
129: * The values are copied using a deep copy.
130: * @param dataset The Dataset to copy.
131: */
132: public final void set(Dataset dataset) {
133:
134: needsUpdate = true;
135: constructor(dataset.getNumSets(), dataset.getNumCats(), dataset
136: .getNumItems());
137: int numSets = getNumSets();
138: int numCats = getNumCats();
139: int numItems = getNumItems();
140: for (int i = 0; i < numSets; ++i) {
141: for (int j = 0; j < numCats; ++j) {
142: for (int k = 0; k < numItems; ++k) {
143: set(i, j, k, dataset.get(i, j, k));
144: }
145: }
146: }
147: }
148:
149: /**
150: * Resets the size of the dataset. Similar to the constructor Dataset (sets, cats, items).
151: * If current size is smaller, then fills with zeroes.
152: * @param sets The number of sets.
153: * @param cats The number of categories per set.
154: * @param items The number of items per category and per set.
155: */
156: public final void setSize(int sets, int cats, int items) {
157:
158: needsUpdate = true;
159: //correct number of sets
160: if (setVector.size() > sets)
161: setVector.setSize(sets);
162: for (int i = setVector.size(); i < sets; ++i)
163: setVector.add(new Vector(cats, 10));
164: //correct number of cats in sets
165: for (int i = 0; i < sets; ++i) {
166: Vector catVector = (Vector) setVector.get(i);
167: if (catVector.size() > cats)
168: catVector.setSize(cats);
169: for (int j = catVector.size(); j < cats; ++j)
170: catVector.add(new Vector(items, 10));
171: }
172: //correct number of items in cats
173: for (int i = 0; i < sets; ++i) {
174: Vector catVector = (Vector) setVector.get(i);
175: for (int j = 0; j < cats; ++j) {
176: Vector itemVector = (Vector) catVector.get(j);
177: if (itemVector.size() > items)
178: itemVector.setSize(items);
179: for (int k = itemVector.size(); k < items; ++k)
180: itemVector.add(new Float(0f));
181: }
182: }
183: }
184:
185: /**
186: * Gets the value for the variable in the set specified by set, the
187: * category specified by cat within the set, and the item specified by item
188: * within the category. If internal resources have not yet been allocated to
189: * contain such a variable a null pointer error may occurr.
190: * @param set The specified set of the variable.
191: * @param cat The specified cat of the variable.
192: * @param item The specified item of the variable.
193: * @return float The value of this variable.
194: */
195: public final float get(int set, int cat, int item) {
196:
197: return ((Float) ((Vector) ((Vector) setVector.get(set))
198: .get(cat)).get(item)).floatValue();
199: }
200:
201: /**
202: * Gets the number of sets of data in this dataset.
203: * @return int The number of sets of this dataset.
204: */
205: public final int getNumSets() {
206: return setVector.size();
207: }
208:
209: /**
210: * Gets the number of categories per set of data in this dataset. This
211: * method requires that the dataset be valid according to the method
212: * validate(). If not, then a null pointer error may occurr.
213: * @return int The number of cats per set of this dataset.
214: */
215: public final int getNumCats() {
216:
217: if (getNumSets() > 0)
218: return ((Vector) setVector.get(0)).size();
219: else
220: return 0;
221: }
222:
223: /**
224: * Gets the number of items per category of data in this dataset. This
225: * method requires that the dataset be valid according to the method
226: * validate(). If not, then a null pointer error may occurr.
227: * @return int The number of items per category of this dataset.
228: */
229: public final int getNumItems() {
230:
231: if (getNumSets() > 0 && getNumCats() > 0) {
232: return ((Vector) (((Vector) setVector.get(0))).get(0))
233: .size();
234: } else
235: return 0;
236: }
237:
238: /**
239: * Gets the greatest value of all the data in the datset. If the dataset
240: * is invalid or empty, then returns Float.MIN_VALUE;
241: * @return float The greatest value in the dataset.
242: */
243: public final float getGreatest() {
244:
245: update();
246: return greatest;
247: }
248:
249: /**
250: * Gets the least value of all the data in the datset. If the dataset
251: * is invalid or empty, then returns Float.MAX_VALUE;
252: * @return float The least value in the dataset.
253: */
254: public final float getLeast() {
255:
256: update();
257: return least;
258: }
259:
260: /**
261: * Gets the average of some set of numbers.
262: * There are three possibilities.
263: * If you want the average of all data items in a particular set and cat, then pass -1 for item.
264: * If you want the average of all data items in a particular set and item, then pass -1 for cat.
265: * If you want the average of all data items in a particular cat and item, then pass -1 for set.
266: * Note, only one -1 may be passed in over all three parameters.
267: * @param set Which particular set or -1 for all.
268: * @param cat Which particular cat or -1 for all.
269: * @param item Which particular item or -1 for all.
270: */
271: public final float getAverage(int set, int cat, int item) {
272:
273: float average = 0f;
274: if (getNumSets() > 0 && getNumCats() > 0 && getNumItems() > 0) {
275: if (set == -1) {
276: float sum = 0f;
277: for (int i = 0; i < getNumSets(); ++i) {
278: sum += get(i, cat, item);
279: }
280: average = sum / getNumSets();
281: }
282: if (cat == -1) {
283: float sum = 0f;
284: for (int j = 0; j < getNumCats(); ++j) {
285: sum += get(set, j, item);
286: }
287: average = sum / getNumCats();
288: }
289: if (item == -1) {
290: float sum = 0f;
291: for (int k = 0; k < getNumItems(); ++k) {
292: sum += get(set, cat, k);
293: }
294: average = sum / getNumItems();
295: }
296: }
297: return average;
298: }
299:
300: /**
301: * Adds a value to the dataset increasing its internal data structure if necessary.
302: * @param set The specified set of the variable.
303: * @param cat The specified cat of the variable.
304: * @param item The specified item of the variable.
305: * @param value The specified value of the variable.
306: */
307: public final void add(int set, int cat, int item, float value) {
308:
309: needsUpdate = true;
310: for (int i = setVector.size(); i <= set; ++i) {
311: setVector.add(new Vector(10, 10));
312: }
313: Vector catVector = (Vector) setVector.get(set);
314: for (int i = catVector.size(); i <= cat; ++i) {
315: catVector.add(new Vector(10, 10));
316: }
317: Vector itemVector = (Vector) catVector.get(cat);
318: for (int i = itemVector.size(); i <= item; ++i) {
319: itemVector.add(new Float(0f));
320: }
321: itemVector.set(item, new Float(value));
322: }
323:
324: /**
325: * Analyzes the (input) dataset and adds moving average trend data to this dataset.
326: * Moving averages are computed by taking some odd number of adjacent items, computing their
327: * average and doing that from left to right for each item. How many adjacent items are used in
328: * computing the moving average is specified in the scope parameter.
329: * @param dataset The dataset to analyze.
330: * @param scope The number of data points to compute over.
331: */
332: public final void addMovingAverage(Dataset dataset, int scope) {
333:
334: //make sure there is enough data
335: int numItemsTotal = dataset.getNumSets() > 0 ? dataset
336: .getNumCats()
337: * dataset.getNumItems() : 0;
338:
339: if (numItemsTotal >= 3) { //do moving average calculation
340:
341: //make sure scope is valid
342: if (scope < 3)
343: scope = 3;
344: else {
345: if (scope > numItemsTotal)
346: scope = numItemsTotal;
347: if (scope % 2 == 0)
348: scope--;
349: }
350:
351: //begin moving average calculation
352: int internalSetOffset = getNumSets(); //offset in case this object holds other sets of data
353: int itemOffset = (scope / 2); //location of first real moving average data
354: Vector itemGroup = new Vector(scope); //create enough data holders
355:
356: //prepare for first real moving average data
357: ItemsForSet items = new ItemsForSet(dataset, 0, 0);
358: itemGroup.add(items);
359: for (int i = 0; i < scope - 1; ++i) {
360: items = items.getNextItemsForSet();
361: itemGroup.add(items);
362: }
363:
364: //get the first moving average data (guaranteed)
365: ItemsForSet firstItems = (ItemsForSet) itemGroup.get(0); //hold first item for later
366: items = (ItemsForSet) itemGroup.get(itemOffset);
367: float firstAverage = getAverageOfItemsForSetGroup(itemGroup);
368: add(internalSetOffset, items.getCat(), items.getItem(),
369: firstAverage);
370: itemGroup.remove(0);
371: itemGroup.add(((ItemsForSet) itemGroup.lastElement())
372: .getNextItemsForSet()); //maybe null
373:
374: //get the rest except for the last real moving average data
375: for (int i = itemOffset + 1; i < numItemsTotal - itemOffset
376: - 1; ++i) {
377: items = (ItemsForSet) itemGroup.get(itemOffset);
378: add(internalSetOffset, items.getCat(), items.getItem(),
379: getAverageOfItemsForSetGroup(itemGroup));
380: itemGroup.remove(0);
381: itemGroup.add(((ItemsForSet) itemGroup.lastElement())
382: .getNextItemsForSet());
383: }
384:
385: //check if only one real moving average data
386: float lastAverage = firstAverage;
387: ItemsForSet lastItems = items.getNextItemsForSet(); //okay for 3,3
388: if (numItemsTotal > scope) {
389:
390: //get the last real moving average data
391: items = (ItemsForSet) itemGroup.get(itemOffset);
392: lastAverage = getAverageOfItemsForSetGroup(itemGroup);
393: add(internalSetOffset, items.getCat(), items.getItem(),
394: lastAverage);
395: lastItems = items.getNextItemsForSet();
396: }
397:
398: //populate beginning with fake moving average data
399: for (int i = 0; i < itemOffset; ++i) {
400: add(internalSetOffset, firstItems.getCat(), firstItems
401: .getItem(), firstAverage);
402: firstItems = firstItems.getNextItemsForSet();
403: }
404:
405: //populate end with fake moving average data
406: for (int i = numItemsTotal - itemOffset; i < numItemsTotal; ++i) {
407: add(internalSetOffset, lastItems.getCat(), lastItems
408: .getItem(), lastAverage);
409: lastItems = lastItems.getNextItemsForSet();
410: }
411: } else if (numItemsTotal > 0) { //do standard average calculation
412: int currSet = setVector.size();
413: for (int j = 0; j < dataset.getNumCats(); ++j) {
414: for (int k = 0; k < dataset.getNumItems(); ++k) {
415: add(currSet, j, k, dataset.getAverage(-1, j, k));
416: }
417: }
418: needsUpdate = true;
419: }
420: }
421:
422: /**
423: * Analyzes the (input) dataset and sets moving average trend data to this dataset.
424: * Moving averages are computed by taking some odd number of adjacent items, computing their
425: * average and doing that from left to right for each item. How many adjacent items are used in
426: * computing the moving average is specified in the scope parameter.
427: * Assumes this dataset already has the same number of cats and items, and that the set exists.
428: * @param set The set into this dataset to put the moving average.
429: * @param dataset The dataset to analyze.
430: * @param scope The number of data points to compute over.
431: */
432: public final void setMovingAverage(int set, Dataset dataset,
433: int scope) {
434:
435: //make sure there is enough data
436: int numItemsTotal = dataset.getNumSets() > 0 ? dataset
437: .getNumCats()
438: * dataset.getNumItems() : 0;
439:
440: if (numItemsTotal >= 3) { //do moving average calculation
441:
442: //make sure scope is valid
443: if (scope < 3)
444: scope = 3;
445: else {
446: if (scope > numItemsTotal)
447: scope = numItemsTotal;
448: if (scope % 2 == 0)
449: scope--;
450: }
451:
452: //begin moving average calculation
453: int internalSetOffset = set; //offset in case this object holds other sets of data
454: int itemOffset = (scope / 2); //location of first real moving average data
455: Vector itemGroup = new Vector(scope); //create enough data holders
456:
457: //prepare for first real moving average data
458: ItemsForSet items = new ItemsForSet(dataset, 0, 0);
459: itemGroup.add(items);
460: for (int i = 0; i < scope - 1; ++i) {
461: items = items.getNextItemsForSet();
462: itemGroup.add(items);
463: }
464:
465: //get the first moving average data (guaranteed)
466: ItemsForSet firstItems = (ItemsForSet) itemGroup.get(0); //hold first item for later
467: items = (ItemsForSet) itemGroup.get(itemOffset);
468: float firstAverage = getAverageOfItemsForSetGroup(itemGroup);
469: set(internalSetOffset, items.getCat(), items.getItem(),
470: firstAverage);
471: itemGroup.remove(0);
472: itemGroup.add(((ItemsForSet) itemGroup.lastElement())
473: .getNextItemsForSet()); //maybe null
474:
475: //get the rest except for the last real moving average data
476: for (int i = itemOffset + 1; i < numItemsTotal - itemOffset
477: - 1; ++i) {
478: items = (ItemsForSet) itemGroup.get(itemOffset);
479: set(internalSetOffset, items.getCat(), items.getItem(),
480: getAverageOfItemsForSetGroup(itemGroup));
481: itemGroup.remove(0);
482: itemGroup.add(((ItemsForSet) itemGroup.lastElement())
483: .getNextItemsForSet());
484: }
485:
486: //check if only one real moving average data
487: float lastAverage = firstAverage;
488: ItemsForSet lastItems = items.getNextItemsForSet(); //okay for 3,3
489: if (numItemsTotal > scope) {
490:
491: //get the last real moving average data
492: items = (ItemsForSet) itemGroup.get(itemOffset);
493: lastAverage = getAverageOfItemsForSetGroup(itemGroup);
494: set(internalSetOffset, items.getCat(), items.getItem(),
495: lastAverage);
496: lastItems = items.getNextItemsForSet();
497: }
498:
499: //populate beginning with fake moving average data
500: for (int i = 0; i < itemOffset; ++i) {
501: set(internalSetOffset, firstItems.getCat(), firstItems
502: .getItem(), firstAverage);
503: firstItems = firstItems.getNextItemsForSet();
504: }
505:
506: //populate end with fake moving average data
507: for (int i = numItemsTotal - itemOffset; i < numItemsTotal; ++i) {
508: set(internalSetOffset, lastItems.getCat(), lastItems
509: .getItem(), lastAverage);
510: lastItems = lastItems.getNextItemsForSet();
511: }
512: } else if (numItemsTotal > 0) { //do standard average calculation
513: int currSet = setVector.size();
514: for (int j = 0; j < dataset.getNumCats(); ++j) {
515: for (int k = 0; k < dataset.getNumItems(); ++k) {
516: set(currSet, j, k, dataset.getAverage(-1, j, k));
517: }
518: }
519: needsUpdate = true;
520: }
521: }
522:
523: /**
524: * Removes values from the dataset. Depending on the values of the paramters,
525: * eight different operations may result. The simplest is to remove a
526: * particular item from a particular set and a particular category. But one
527: * can also perform more complex operations. For example, one can remove
528: * every set of data. The key to modifying the behavior is passing in
529: * a negative one or -1 value for a parameter. A -1 value specifies that
530: * whatever is to be removed, will be removed for all such things
531: * (ie sets, cats, or items) corresponding to that parameter. So if one
532: * wanted to remove every set of data, one could pass -1 to set, to cat, and
533: * to item. This would be specify to remove all items, for all cats, for all
534: * sets. If one wanted to remove only a single item from a particular set and
535: * a particular category, then one would pass a non -1 value for each of the
536: * parameters. Below is a listing of the options and a short description.
537: * The letters "a", "b", "c" indicates a non -1 value.<br>
538: * set= -1, cat= -1, item= -1 Removes every set of data.<br>
539: * set= a, cat= -1, item= -1 Removes a particular set of data.<br>
540: * set= -1, cat= a, item= -1 Removes a particular category of data for every
541: * set.<br>
542: * set= a, cat= b, item= -1 Removes a particular category of data for a
543: * particular set.<br>
544: * set= -1, cat= -1, item= a Removes a particular item for every set and
545: * every category.<br>
546: * set= -1, cat= a, item= b Removes a particular item of a particular
547: * category in every set.<br>
548: * set= a, cat= -1, item= b Removes a particular item of a particular set in
549: * every category.<br>
550: * set= a, cat= b, item= c Removes a particular item of a particular category
551: * of a particular set.<br>
552: * @param set The set of data within which data is to be removed.
553: * @param cat The cat of data within which data is to be removed.
554: * @param item The item of data that is to be removed.
555: */
556: public final void remove(int set, int cat, int item) {
557:
558: needsUpdate = true;
559:
560: //remove all sets
561: if (set == -1 && cat == -1 && item == -1) {
562: constructor(0, 0, 0);
563: return;
564: }
565:
566: int numSets = getNumSets();
567: int numCats = getNumCats();
568: int numItems = getNumItems();
569: if (set != -1 && set >= numSets)
570: return;
571: if (cat != -1 && cat >= numCats)
572: return;
573: if (item != -1 && item >= numItems)
574: return;
575:
576: //removes a particular set
577: if (set != -1 && cat == -1 && item == -1) {
578: setVector.remove(set);
579: }
580: //removes a cat for all sets
581: else if (set == -1 && cat != -1 && item == -1) {
582: for (int i = 0; i < numSets; ++i) {
583: ((Vector) setVector.get(i)).remove(cat);
584: }
585: }
586: //removes a cat for a particular set
587: else if (set != 1 && cat != 1 && item == -1) {
588: ((Vector) setVector.get(set)).remove(cat);
589: }
590: //removes an item for all sets/cats
591: else if (set == -1 && cat == -1 && item != 1) {
592: for (int i = 0; i < numSets; ++i) {
593: Vector catVector = (Vector) setVector.get(i);
594: for (int j = 0; j < numCats; ++i) {
595: ((Vector) catVector.get(j)).remove(item);
596: }
597: }
598: }
599: //removes an item for all sets and a particular cat
600: else if (set == -1 && cat != -1 && item != -1) {
601: for (int i = 0; i < numSets; ++i) {
602: ((Vector) ((Vector) setVector.get(i)).get(cat))
603: .remove(item);
604: }
605: }
606: //removes an item for all cats and a particular set
607: else if (set != -1 && cat == -1 && item != -1) {
608: for (int j = 0; j < numCats; ++j) {
609: ((Vector) ((Vector) setVector.get(set)).get(j))
610: .remove(item);
611: }
612: }
613: //removes an item for a particular set/cat
614: else if (set != -1 && cat != -1 && item != -1) {
615: ((Vector) ((Vector) setVector.get(set)).get(cat))
616: .remove(item);
617: }
618: }
619:
620: /**
621: * Shifts all the data items one place, from the higher order to the lower
622: * order, replacing the highest order items with the specified items. This
623: * method is designed to be used with graph charts where the data is
624: * dynamically updated and graphed over time categories. For example, if you
625: * picture a normal line chart with say three sets of data, that charts the
626: * amount of memory being used by various programs on your computer and is
627: * updated every second. The number of lines would correspond to the number
628: * of programs being charted. The number of x axis labels would refer to the
629: * number of seconds of data is being charted (ex 10 seconds). Every second,
630: * we could call shiftLower and this would shift left all the old data in the
631: * chart, and shift in to the right of the chart, some new data. There would
632: * have to be a new data value for each line in the chart. The the number of
633: * lines corresponds to the number of programs, and the number of programs,
634: * corresponds to the number of legend labels, etc, the number of data values
635: * shifted in must be equal to the number of sets in the dataset.
636: * @param values An array of values of length getNumSets() to shift in.
637: */
638: public final void doShiftLower(float[] values) {
639: needsUpdate = true;
640: for (int i = 0; i < setVector.size() && i < values.length; ++i) {
641: Vector catVector = (Vector) setVector.get(i);
642: for (int j = 0; j < catVector.size() - 1; ++j) {
643: Vector itemVector = (Vector) catVector.get(j);
644: itemVector.remove(0);
645: Vector itemVectorNext = (Vector) catVector.get(j + 1);
646: itemVector.add(itemVectorNext.get(0));
647: }
648: Vector itemVector = (Vector) catVector
649: .get(catVector.size() - 1);
650: itemVector.remove(0);
651: itemVector.add(new Float(values[i]));
652: }
653: }
654:
655: /**
656: * Converts the dataset for use in "stacked" charts. This is a convenience
657: * method. Stacked charts are those where each set of data is to be stacked
658: * on top of the previous set. So if you have multiple sets of data and you
659: * want the graph components corresponding to each set to be stacked on top
660: * of the previous set but you don't want to have to adjust your data
661: * yourself to get this ***affect*** then use this method. Multiple calls
662: * to this method will change your dataset each time. You will only want to call it once. Unless
663: * you repopulate your whole dataset with virgin (i.e. never before stacked) values again.
664: */
665: public final void doConvertToStacked() {
666:
667: needsUpdate = true;
668: for (int j = 0; j < getNumCats(); ++j) {
669: for (int k = 0; k < getNumItems(); ++k) {
670: float posSum = 0f;
671: float negSum = 0f;
672: for (int i = 0; i < getNumSets(); ++i) {
673: float item = get(i, j, k);
674: if (item >= 0f) {
675: posSum = posSum + get(i, j, k);
676: set(i, j, k, posSum);
677: } else {
678: negSum = negSum + get(i, j, k);
679: set(i, j, k, negSum);
680: }
681: }
682: }
683: }
684: }
685:
686: /**
687: * Gets a float[][] representation of this dataset for use by GraphChartArea.
688: * @return float[][] A representation of this dataset.
689: */
690: final float[][] getOldGraphStruct() {
691:
692: float[][] dataset;
693: if (setVector.size() == 0)
694: dataset = new float[0][0];
695: else {
696: dataset = new float[setVector.size()][((Vector) setVector
697: .get(0)).size()
698: * ((Vector) ((Vector) setVector.get(0)).get(0))
699: .size()];
700: for (int i = 0; i < setVector.size(); ++i) {
701: Vector catVector = (Vector) setVector.get(i);
702: for (int j = 0; j < catVector.size(); ++j) {
703: Vector itemVector = (Vector) catVector.get(j);
704: for (int k = 0; k < itemVector.size(); ++k) {
705: dataset[i][j * itemVector.size() + k] = ((Float) itemVector
706: .get(k)).floatValue();
707: }
708: }
709: }
710: }
711: return dataset;
712: }
713:
714: /**
715: * Gets a float[] representation of this dataset for use by PieChartArea.
716: * @return float[] A representation of this dataset.
717: */
718: final float[] getOldPieStruct() {
719:
720: float[] dataset = new float[setVector.size()];
721: for (int i = 0; i < setVector.size(); ++i) {
722: float sum = 0;
723: Vector catVector = (Vector) setVector.get(i);
724: for (int j = 0; j < catVector.size(); ++j) {
725: Vector itemVector = (Vector) catVector.get(j);
726: for (int k = 0; k < itemVector.size(); ++k) {
727: sum += ((Float) itemVector.get(k)).floatValue();
728: }
729: }
730: dataset[i] = sum;
731: }
732: return dataset;
733: }
734:
735: /**
736: * Gets whether this object needs to be updated with new properties.
737: * @param chart2D The object that may need to be updated.
738: * @return If true then needs update.
739: */
740: final boolean getChart2DNeedsUpdate(Chart2D chart2D) {
741: if (needsUpdate)
742: return true;
743: int index = -1;
744: if ((index = chart2DVector.indexOf(chart2D)) != -1) {
745: return ((Boolean) needsUpdateVector.get(index))
746: .booleanValue();
747: }
748: return false;
749: }
750:
751: /**
752: * Adds a Chart2D to the set of objects using these properties.
753: * @param chart2D The object to add.
754: */
755: final void addChart2D(Chart2D chart2D) {
756:
757: if (!chart2DVector.contains(chart2D)) {
758: chart2DVector.add(chart2D);
759: needsUpdateVector.add(new Boolean(true));
760: }
761: }
762:
763: /**
764: * Removes a Chart2D from the set of objects using these properties.
765: * @param chart2D The object to remove.
766: */
767: final void removeChart2D(Chart2D chart2D) {
768:
769: int index = -1;
770: if ((index = chart2DVector.indexOf(chart2D)) != -1) {
771: chart2DVector.remove(index);
772: needsUpdateVector.remove(index);
773: }
774: }
775:
776: /**
777: * Validates the properties of this object.
778: * If debug is true then prints a messages indicating whether each property is valid.
779: * Returns true if all the properties were valid and false otherwise.
780: * @param debug If true then will print status messages.
781: * @return If true then valid.
782: */
783: final boolean validate(boolean debug) {
784:
785: if (debug)
786: System.out.println("Validating Dataset");
787:
788: boolean valid = true;
789:
790: int numSets = setVector.size();
791: int numCats = 0;
792: Vector catVector = null;
793: if (numSets > 0) {
794: catVector = (Vector) setVector.get(0);
795: numCats = catVector.size();
796: }
797: int numItems = 0;
798: Vector itemVector = null;
799: if (numCats > 0) {
800: itemVector = (Vector) catVector.get(0);
801: numItems = itemVector.size();
802: }
803:
804: for (int i = 0; i < numSets && valid; ++i) {
805: catVector = (Vector) setVector.get(i);
806: if (numCats != catVector.size()) {
807: valid = false;
808: break;
809: }
810: for (int j = 0; j < catVector.size() && valid; ++j) {
811: itemVector = (Vector) catVector.get(j);
812: if (numItems != itemVector.size()) {
813: valid = false;
814: break;
815: }
816: }
817: }
818:
819: if (debug && !valid)
820: System.out.println("Problem with Dataset");
821:
822: if (debug) {
823: if (valid)
824: System.out.println("Dataset was valid");
825: else
826: System.out.println("Dataset was invalid");
827: }
828:
829: return valid;
830: }
831:
832: /**
833: * Updates the properties of this Chart2D.
834: * @param chart2D The object to update.
835: */
836: final void updateChart2D(Chart2D chart2D) {
837:
838: if (getChart2DNeedsUpdate(chart2D)) {
839:
840: update();
841:
842: int index = -1;
843: if ((index = chart2DVector.indexOf(chart2D)) != -1) {
844: needsUpdateVector.set(index, new Boolean(false));
845: }
846: }
847: }
848:
849: private void constructor(int sets, int cats, int items) {
850:
851: needsUpdate = true;
852: setVector = new Vector(sets, 10);
853: for (int i = 0; i < sets; ++i) {
854: Vector catVector = new Vector(cats, 10);
855: setVector.add(i, catVector);
856: for (int j = 0; j < cats; ++j) {
857: Vector itemVector = new Vector(items, 10);
858: catVector.add(j, itemVector);
859: for (int k = 0; k < items; ++k) {
860: itemVector.add(k, new Float(0f));
861: }
862: }
863: }
864: }
865:
866: private void update() {
867:
868: if (needsUpdate) {
869:
870: for (int i = 0; i < needsUpdateVector.size(); ++i) {
871: needsUpdateVector.set(i, new Boolean(true));
872: }
873: needsUpdate = false;
874:
875: greatest = -9999999999999999f;
876: least = 9999999999999999f;
877: for (int i = 0; i < setVector.size(); ++i) {
878: Vector catVector = (Vector) setVector.get(i);
879: for (int j = 0; j < catVector.size(); ++j) {
880: Vector itemVector = (Vector) catVector.get(j);
881: for (int k = 0; k < itemVector.size(); ++k) {
882: float value = ((Float) itemVector.get(k))
883: .floatValue();
884: greatest = value > greatest ? value : greatest;
885: least = value < least ? value : least;
886: }
887: }
888: }
889: if (greatest < least)
890: greatest = least = 0;
891: }
892: }
893:
894: private float getAverageOfItemsForSetGroup(Vector group) {
895:
896: float sum = 0f;
897: for (int i = 0; i < group.size(); ++i) {
898: sum += ((ItemsForSet) group.get(i)).getSum();
899: }
900: return sum / group.size();
901: }
902:
903: private class ItemsForSet {
904:
905: private int cat;
906: private int item;
907: private Dataset dataset;
908:
909: ItemsForSet(Dataset dataset, int cat, int item) {
910:
911: this .cat = cat;
912: this .item = item;
913: this .dataset = dataset;
914: }
915:
916: private int getCat() {
917: return cat;
918: }
919:
920: private int getItem() {
921: return item;
922: }
923:
924: private float getSum() {
925: float sum = 0;
926: int numSets = dataset.getNumSets();
927: for (int i = 0; i < numSets; ++i) {
928: sum += dataset.get(i, cat, item);
929: }
930: return sum;
931: }
932:
933: private ItemsForSet getNextItemsForSet() {
934: int nextCat = cat;
935: int nextItem = item;
936: if (item >= (dataset.getNumItems() - 1)) {
937: nextCat++;
938: nextItem = 0;
939: } else {
940: nextItem++;
941: }
942: if (nextCat >= dataset.getNumCats())
943: return null;
944: else
945: return new ItemsForSet(dataset, nextCat, nextItem);
946: }
947: }
948: }
|