KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > chart2d > LBGraphArea


1 /**
2  * Chart2D, a java library for drawing two dimensional charts.
3  * Copyright (C) 2001 Jason J. Simas
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * The author of this library may be contacted at:
19  * E-mail: jjsimas@users.sourceforge.net
20  * Street Address: J J Simas, 887 Tico Road, Ojai, CA 93023-3555 USA
21  */

22
23
24 package net.sourceforge.chart2d;
25
26
27 import java.awt.*;
28 import java.awt.geom.*;
29 import java.util.*;
30
31
32 /**
33  * The area that the x axis and y axis attach to, and in which bars, lines, or
34  * dots are painted to represent the data set. The "LB" in the name of this
35  * class stands for Labels Bottom. This graph should always be used when the
36  * data descriptors for the the graph are located on the bottom. Data
37  * descriptors have values such as September and November, and not like 0 or 50.
38  * <b>Features:</b><br>
39  * Provides bordering functionality. Allows lines to be painted that match
40  * up with y axis and x axis tick marks in order to know where the bars, dots,
41  * or plot lines are in relation to the y axis values. Supports vertical
42  * bar graphs, line charts, and scatter charts (dots), and any combintation
43  * thereof. If a bars are present in any graph, then it is highly recommended
44  * that the GraphArea method, allowComponentAlignment be passed the value of
45  * false. This will ensure that all the bars are visible on the graph. For
46  * graphs that do not have bars present, the opposite is advised. The GraphArea
47  * method should be passed the value of true. This provides a more intuitive
48  * representation of the data.
49  */

50 final class LBGraphArea extends GraphArea {
51
52   private FancyBar[][] bars;
53   private FancyDot[][] dots;
54   private FancyLine[] lines;
55   private boolean needsUpdate;
56
57
58   /**
59    * Creates a graph area with GraphArea's default values. Default values:
60    * resetLBGraphAreaModel (true);
61    */

62   LBGraphArea() {
63
64     bars = new FancyBar[0][0];
65     dots = new FancyDot[0][0];
66     lines = new FancyLine[0];
67
68     setBarRoundingRatio (.25f);
69
70     resetLBGraphAreaModel (true);
71     needsUpdate = true;
72   }
73
74
75   /**
76    * Does a quick calculation of the preferred width of the graph.
77    * @param numSets The number of data series.
78    * @param numComps The number of data categories per series.
79    * @param numCompsPerCat The number of data per series, per category.
80    * @return The preferred width.
81    */

82   final int getPrefSpaceWidth (int numSets, int numCats, int numCompsPerCat) {
83
84     float ratio = getRatio (WIDTH);
85     int barsWidth = 0, dotsWidth = 0, linesWidth = 0, prefWidth = 0;
86     if (getAllowComponentAlignment()) {
87       barsWidth = getBarsExistence() ?
88         (int)(numCats * (1 + (1 - getBarsWithinCategoryOverlapRatio()) *
89         (numCompsPerCat - 1)) *
90         applyRatio (getBarsThicknessModel(), ratio)) : 0;
91       dotsWidth = getDotsExistence() ?
92         (int)(numCats * (1 + (1 - getDotsWithinCategoryOverlapRatio()) *
93         (numCompsPerCat - 1)) *
94         applyRatio (getDotsThicknessModel(), ratio)) : 0;
95       linesWidth = getLinesExistence() ?
96         (int)(numCats * (1 + (1 - getLinesWithinCategoryOverlapRatio()) *
97         (numCompsPerCat - 1)) *
98         applyRatio (getLinesThicknessModel(), ratio)) : 0;
99     }
100     else {
101       barsWidth = getBarsExistence() ?
102         (int)(numSets * numCats *
103         (1 + (1 - getBarsWithinCategoryOverlapRatio()) *
104         (numCompsPerCat - 1)) *
105         applyRatio (getBarsThicknessModel(), ratio)) : 0;
106       dotsWidth = getDotsExistence() ?
107         (int)(numSets * numCats * (1 +
108         (1 - getDotsWithinCategoryOverlapRatio()) *
109         (numCompsPerCat - 1)) *
110         applyRatio (getDotsThicknessModel(), ratio)) : 0;
111       linesWidth = getLinesExistence() ?
112         (int)(numSets * numCats * (1 +
113         (1 - getLinesWithinCategoryOverlapRatio()) *
114         (numCompsPerCat - 1)) *
115         applyRatio (getLinesThicknessModel(), ratio)) : 0;
116
117     }
118     prefWidth = barsWidth > dotsWidth ? barsWidth : dotsWidth;
119     prefWidth = prefWidth > linesWidth ? prefWidth : linesWidth;
120     int gapsWidth = getBetweenComponentsGapExistence() ?
121       numCats * applyRatio (getBetweenComponentsGapThicknessModel(), ratio) : 0;
122     prefWidth = prefWidth + gapsWidth;
123     prefWidth = prefWidth < getSpaceSize(MAX).width ? prefWidth : getSpaceSize(MAX).width;
124     return (prefWidth);
125   }
126
127
128   /**
129    * Indicates whether some property of this class has changed.
130    * @return True if some property has changed.
131    */

132   final boolean getLBGraphAreaNeedsUpdate() {
133
134     return (needsUpdate || getGraphAreaNeedsUpdate());
135   }
136
137
138   /**
139    * Resets the model for this class. The model is used for shrinking and
140    * growing of its components based on the maximum size of this class. If this
141    * method is called, then the next time the maximum size is set, this classes
142    * model maximum size will be made equal to the new maximum size. Effectively
143    * what this does is ensure that whenever this objects maximum size is equal
144    * to the one given, then all of the components will take on their default
145    * model sizes. Note: This is only useful when auto model max sizing is
146    * disabled.
147    * @param reset True causes the max model size to be set upon the next max
148    * sizing.
149    */

150   final void resetLBGraphAreaModel (boolean reset) {
151
152     needsUpdate = true;
153     resetGraphAreaModel (reset);
154   }
155
156
157   /**
158    * Updates this parent's variables, and this' variables.
159    */

160   final void updateLBGraphArea() {
161
162     if (getLBGraphAreaNeedsUpdate()) {
163       updateGraphArea();
164       update();
165     }
166     needsUpdate = false;
167   }
168
169
170   /**
171    * Paints all the components of this class. First all variables are updated.
172    * @param g2D The graphics context for calculations and painting.
173    */

174   final void paintComponent (Graphics2D g2D) {
175
176     updateLBGraphArea();
177     super.paintComponent (g2D);
178
179     Shape oldClip = g2D.getClip();
180     if (getClip()) g2D.setClip (new Rectangle (getSpaceSizeLocation (MIN), getSpaceSize (MIN)));
181
182     Composite oldComposite = g2D.getComposite();
183     g2D.setComposite (getComponentsAlphaComposite());
184
185     int numBars = bars.length > 0 ? bars[0].length : 0;
186     int numDots = dots.length > 0 ? dots[0].length : 0;
187     int numLines = lines.length;
188     int numSets = getGraphValues().length;
189     if (!getAllowComponentAlignment() && numBars > 0) {
190       for (int i = 0; i < numSets; ++i) {
191         for (int j = 0; j < numBars; ++j) {
192           bars[i][j].paint (g2D);
193         }
194       }
195     }
196     else if (numBars > 0) {
197       int[][] graphValues = getGraphValues();
198       graphValues = stackedBarConvert (graphValues, getBarLowValues());
199       for (int j = 0; j < numBars; ++j) {
200         int[] sorted = stackedBarSort (graphValues, j);
201         for (int i = 0; i < numSets; ++i) {
202           bars[sorted[i]][j].paint (g2D);
203         }
204       }
205     }
206
207     for (int i = numLines - 1; i >= 0 ; --i) lines[i].paint (g2D);
208
209     for (int i = numSets - 1; i >= 0; --i) {
210       for (int j = numDots - 1; j >= 0 ; --j) {
211         dots[i][j].paint (g2D);
212       }
213     }
214
215     g2D.setClip (oldClip);
216     g2D.setComposite (oldComposite);
217   }
218
219
220   private void update() {
221
222     if (getAllowComponentAlignment()) updateAllowAlignment();
223     else updateDisallowAlignment();
224   }
225
226
227   private void updateDisallowAlignment() {
228
229     int[][] graphLengths = getGraphValues();
230     int numBars = 0, numDots = 0, numLinePoints = 0, numGaps = 0;
231     int numSets = graphLengths.length;
232     Rectangle[] xTicks = getXTicks();
233     int numCompsPerCat = 0;
234     int numCats = 0;
235     if (numSets > 0) {
236       numCats = getLabelsAxisTicksAlignment() == BETWEEN ?
237         xTicks.length + 1 : xTicks.length;
238       numBars = getBarsExistence() ? numCats : 0;
239       numDots = getDotsExistence() ? numCats : 0;
240       numLinePoints = getLinesExistence() ? numCats : 0;
241       numGaps = getBetweenComponentsGapExistence() ? numCats : 0;
242       numCompsPerCat =
243         numCats > 0 ? (int)(graphLengths[0].length / numCats) : 0;
244     }
245     if (numSets > 0 && numCats > 0 && numCompsPerCat > 0) {
246
247       float ratio = getRatio (WIDTH);
248       int availableThickness = getSpaceSize(MIN).width;
249
250       int betweenComponentsGapThickness = 0;
251       if (numGaps > 0) {
252         betweenComponentsGapThickness =
253           applyRatio (getBetweenComponentsGapThicknessModel(), ratio);
254         betweenComponentsGapThickness =
255           numGaps * betweenComponentsGapThickness <= availableThickness ?
256           betweenComponentsGapThickness : availableThickness / numGaps;
257         availableThickness -= numGaps * betweenComponentsGapThickness;
258       }
259
260       int barsThickness = 0;
261       if (numBars > 0) {
262         barsThickness = applyRatio (getBarsThicknessModel(), ratio);
263         if ((numSets * numBars * numCompsPerCat) * barsThickness <=
264           availableThickness) {
265           float leftover =
266             (availableThickness - barsThickness *
267             (numSets * numBars * numCompsPerCat)) /
268             (numSets * numBars * numCompsPerCat);
269           barsThickness =
270             barsThickness + (int)(getBarsExcessSpaceFeedbackRatio() * leftover);
271         }
272         else {
273           float numOverlapBarsPerCat =
274             (1 + (1 - getBarsWithinCategoryOverlapRatio()) *
275             (numCompsPerCat - 1));
276           if (numSets * numBars * numOverlapBarsPerCat * barsThickness >
277             availableThickness) {
278             barsThickness = (int)(availableThickness /
279               (numSets * numBars * numOverlapBarsPerCat));
280           }
281         }
282       }
283       int dotsThickness = 0;
284       if (numDots > 0) {
285         dotsThickness = applyRatio (getDotsThicknessModel(), getRatio (LESSER));
286         if ((numSets * numDots * numCompsPerCat) * dotsThickness <=
287           availableThickness) {
288           float leftover =
289             (availableThickness - dotsThickness *
290             (numSets * numDots * numCompsPerCat)) /
291             (numSets * numDots * numCompsPerCat);
292           dotsThickness =
293             dotsThickness + (int)(getDotsExcessSpaceFeedbackRatio() * leftover);
294         }
295         else {
296           float numOverlapDotsPerCat =
297             (1 + (1 - getDotsWithinCategoryOverlapRatio()) *
298             (numCompsPerCat - 1));
299           if (numSets * numDots * numOverlapDotsPerCat * dotsThickness >
300             availableThickness) {
301             dotsThickness = (int)(availableThickness /
302               (numSets * numDots * numOverlapDotsPerCat));
303           }
304         }
305       }
306       int linesThickness = 0;
307       if (numLinePoints > 0) {
308         linesThickness = applyRatio (getLinesThicknessModel(), getRatio (LESSER));
309         if ((numSets * numLinePoints * numCompsPerCat) * linesThickness <=
310           availableThickness) {
311           float leftover =
312             (availableThickness - linesThickness *
313             (numSets * numLinePoints * numCompsPerCat)) /
314             (numSets * numLinePoints * numCompsPerCat);
315           linesThickness =
316             linesThickness + (int)(getLinesExcessSpaceFeedbackRatio() * leftover);
317         }
318         else {
319           float numOverlapLinesPerCat =
320             (1 + (1 - getLinesWithinCategoryOverlapRatio()) *
321             (numCompsPerCat - 1));
322           if (numSets * numLinePoints * numOverlapLinesPerCat * linesThickness >
323             availableThickness) {
324             linesThickness = (int)(availableThickness /
325               (numSets * numLinePoints * numOverlapLinesPerCat));
326           }
327         }
328       }
329
330       int componentsThickness = barsThickness;
331       componentsThickness = dotsThickness > componentsThickness ?
332         dotsThickness : componentsThickness;
333       componentsThickness = linesThickness > componentsThickness ?
334         linesThickness : componentsThickness;
335       int setsThickness = componentsThickness * numSets;
336       setsThickness =
337         setsThickness > availableThickness / (numCats * numCompsPerCat) ?
338         availableThickness / (numCats * numCompsPerCat) : setsThickness;
339       componentsThickness = setsThickness / numSets;
340
341       Rectangle2D.Float boundsGraph = new Rectangle2D.Float (
342         getSpaceSizeLocation (MIN).x, getSpaceSizeLocation (MIN).y,
343         getSpaceSize (MIN).width, getSpaceSize (MIN).height);
344
345       bars = new FancyBar[numSets][numBars * numCompsPerCat];
346       int[][] barLowValues = getBarLowValues();
347       if (numBars > 0) {
348         int y = getSpaceSizeLocation (MIN).y + getSpaceSize(MIN).height, x = 0;
349         float catDelta =
350           getSpaceSize (MIN).width / (float)(numBars * numCompsPerCat);
351         if (numBars == 1) {
352           x = getSpaceSizeLocation (MIN).x +
353             (getSpaceSize(MIN).width - setsThickness) / 2;
354           x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
355           for (int k = 0; k < numCompsPerCat; ++k) {
356             for (int j = 0; j < numSets; ++j) {
357               int compX = x + j * componentsThickness +
358                 (componentsThickness - barsThickness) / 2;
359               bars[j][k] = new FancyBar();
360               bars[j][k].setDataSign (getDataSign());
361               bars[j][k].setBaseValue (y - getLinesFillInteriorBaseValue());
362               if (graphLengths[j][k] > barLowValues[j][k]) {
363                 bars[j][k].setBounds (new Rectangle2D.Float (
364                   compX + k * catDelta, y - graphLengths[j][k],
365                   barsThickness, graphLengths[j][k] - barLowValues[j][k]));
366               }
367               else {
368                 bars[j][k].setBounds (new Rectangle2D.Float (
369                   compX + k * catDelta, y - barLowValues[j][k],
370                   barsThickness, barLowValues[j][k] - graphLengths[j][k]));
371               }
372               Rectangle2D.Float clipBounds = new Rectangle2D.Float (
373                 boundsGraph.x, bars[j][k].getBounds().y,
374                 boundsGraph.width, bars[j][k].getBounds().height);
375               bars[j][k].setClipBounds (clipBounds);
376               bars[j][k].setLightBounds (
377                 getComponentsLightType() == COMPONENT ? bars[j][k].getBounds() : boundsGraph);
378               bars[j][k].setLightSource (getComponentsLightSource());
379               bars[j][k].setColor (!getComponentsColoringByCat() ?
380                 getBarColors()[j] : getComponentsColorsByCat()[0]);
381               bars[j][k].setOutlineExistence (getOutlineComponents());
382               bars[j][k].setOutlineColor (getOutlineComponentsColor());
383               bars[j][k].setArcw (getBarRoundingRatio() * barsThickness);
384               bars[j][k].setArch (getBarRoundingRatio() * barsThickness);
385               bars[j][k].setWarningRegions (getWarningRegions());
386               bars[j][k].setType (FancyShape.LABELSBOTTOM);
387               bars[j][k].setGraphBounds (boundsGraph);
388             }
389           }
390         }
391         else {
392           float minX, maxX, tickHalfWidth;
393           minX = getSpaceSizeLocation (MIN).x;
394           tickHalfWidth = xTicks[0].width / 2f;
395           maxX = xTicks[0].x + tickHalfWidth;
396           int i = 0;
397           do {
398             if (getLabelsAxisTicksAlignment() == this.BETWEEN) {
399               x = (int)(minX + Math.floor((maxX - minX - setsThickness) / 2f));
400             }
401             else x = (int)Math.floor (maxX - setsThickness / 2f);
402             x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
403             for (int k = 0; k < numCompsPerCat; ++k) {
404               for (int j = 0; j < numSets; ++j) {
405                 int compX = x + j * componentsThickness +
406                   (componentsThickness - barsThickness) / 2;
407                 bars[j][i * numCompsPerCat + k] = new FancyBar();
408                 bars[j][i * numCompsPerCat + k].setDataSign (getDataSign());
409                 bars[j][i * numCompsPerCat + k].setBaseValue (y - getLinesFillInteriorBaseValue());
410                 if (graphLengths[j][i * numCompsPerCat + k] >
411                   barLowValues[j][i * numCompsPerCat + k]) {
412                   bars[j][i * numCompsPerCat + k].setBounds (new Rectangle2D.Float (
413                     compX + k * catDelta, y - graphLengths[j][i * numCompsPerCat + k],
414                     barsThickness, graphLengths[j][i * numCompsPerCat + k] -
415                     barLowValues[j][i * numCompsPerCat + k]));
416                 }
417                 else {
418                   bars[j][i * numCompsPerCat + k].setBounds (new Rectangle2D.Float (
419                     compX + k * catDelta, y - barLowValues[j][i * numCompsPerCat + k],
420                     barsThickness, barLowValues[j][i * numCompsPerCat + k] -
421                     graphLengths[j][i * numCompsPerCat + k]));
422                 }
423                 Rectangle2D.Float clipBounds = new Rectangle2D.Float (
424                   boundsGraph.x, bars[j][i * numCompsPerCat + k].getBounds().y,
425                   boundsGraph.width, bars[j][i * numCompsPerCat + k].getBounds().height);
426                 bars[j][i * numCompsPerCat + k].setClipBounds (clipBounds);
427                 bars[j][i * numCompsPerCat + k].setLightBounds (
428                   getComponentsLightType() == COMPONENT ?
429                   bars[j][i * numCompsPerCat + k].getBounds() : boundsGraph);
430                 bars[j][i * numCompsPerCat + k].setLightSource (getComponentsLightSource());
431                 bars[j][i * numCompsPerCat + k].setColor (!getComponentsColoringByCat() ?
432                   getBarColors()[j] : getComponentsColorsByCat()[i]);
433                 bars[j][i * numCompsPerCat + k].setOutlineExistence (getOutlineComponents());
434                 bars[j][i * numCompsPerCat + k].setOutlineColor (getOutlineComponentsColor());
435                 bars[j][i * numCompsPerCat + k].setArcw (getBarRoundingRatio() * barsThickness);
436                 bars[j][i * numCompsPerCat + k].setArch (getBarRoundingRatio() * barsThickness);
437                 bars[j][i * numCompsPerCat + k].setWarningRegions (getWarningRegions());
438                 bars[j][i * numCompsPerCat + k].setType (FancyShape.LABELSBOTTOM);
439                 bars[j][i * numCompsPerCat + k].setGraphBounds (boundsGraph);
440               }
441             }
442             if (i + 1 == numBars) break;
443             minX = maxX;
444             if (i + 2 == numBars &&
445               getLabelsAxisTicksAlignment() == this.BETWEEN) {
446               maxX = getSpaceSizeLocation (MIN).x + getSpaceSize (MIN).width;
447             }
448             else maxX = xTicks[i + 1].x + tickHalfWidth;
449             ++i;
450           } while (true);
451         }
452       }
453
454       dots = new FancyDot[numSets][numDots * numCompsPerCat];
455       if (numDots > 0) {
456         int y = getSpaceSizeLocation (MIN).y + getSpaceSize(MIN).height -
457           dotsThickness / 2, x;
458         float catDelta =
459           getSpaceSize (MIN).width / (float)(numDots * numCompsPerCat);
460         if (numDots == 1) {
461           x = getSpaceSizeLocation (MIN).x +
462             (getSpaceSize(MIN).width - setsThickness) / 2;
463           x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
464           for (int k = 0; k < numCompsPerCat; ++k) {
465             for (int j = 0; j < numSets; ++j) {
466               int compX = x + j * componentsThickness +
467                 (componentsThickness - dotsThickness) / 2;
468               dots[j][k] = new FancyDot();
469               dots[j][k].setBounds (new Rectangle2D.Float (
470                 compX + k * catDelta, y - graphLengths[j][k], dotsThickness, dotsThickness));
471               dots[j][k].setClipBounds (dots[j][k].getBounds());
472               dots[j][k].setColor (!getComponentsColoringByCat() ?
473                 getDotColors()[j] : getComponentsColorsByCat()[0]);
474               dots[j][k].setOutlineExistence (getOutlineComponents());
475               dots[j][k].setOutlineColor (getOutlineComponentsColor());
476               dots[j][k].setLightBounds (
477                 getComponentsLightType() == COMPONENT ? dots[j][k].getBounds() : boundsGraph);
478               dots[j][k].setLightSource (getComponentsLightSource());
479               dots[j][k].setWarningRegions (getWarningRegions());
480               dots[j][k].setType (FancyShape.LABELSBOTTOM);
481             }
482           }
483         }
484         else {
485           float minX, maxX, tickHalfWidth;
486           minX = getSpaceSizeLocation (MIN).x;
487           tickHalfWidth = xTicks[0].width / 2f;
488           maxX = xTicks[0].x + tickHalfWidth;
489           int i = 0;
490           do {
491             if (getLabelsAxisTicksAlignment() == this.BETWEEN) {
492               x = (int)(minX + Math.floor((maxX - minX - setsThickness) / 2f));
493             }
494             else x = (int)Math.floor (maxX - setsThickness / 2f);
495             x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
496             for (int k = 0; k < numCompsPerCat; ++k) {
497               for (int j = 0; j < numSets; ++j) {
498                 int compX = x + j * componentsThickness +
499                   (componentsThickness - dotsThickness) / 2;
500                 dots[j][i * numCompsPerCat + k] = new FancyDot();
501                 dots[j][i * numCompsPerCat + k].setBounds (new Rectangle2D.Float (
502                   compX + k * catDelta, y - graphLengths[j][i * numCompsPerCat + k],
503                   dotsThickness, dotsThickness));
504                 dots[j][i * numCompsPerCat + k].setClipBounds (
505                   dots[j][i * numCompsPerCat + k].getBounds());
506                 dots[j][i * numCompsPerCat + k].setColor (!getComponentsColoringByCat() ?
507                   getDotColors()[j] : getComponentsColorsByCat()[i]);
508                 dots[j][i * numCompsPerCat + k].setOutlineExistence (getOutlineComponents());
509                 dots[j][i * numCompsPerCat + k].setOutlineColor (getOutlineComponentsColor());
510                 dots[j][i * numCompsPerCat + k].setLightBounds (
511                   getComponentsLightType() == COMPONENT ?
512                   dots[j][i * numCompsPerCat + k].getBounds() : boundsGraph);
513                 dots[j][i * numCompsPerCat + k].setLightSource (getComponentsLightSource());
514                 dots[j][i * numCompsPerCat + k].setWarningRegions (getWarningRegions());
515                 dots[j][i * numCompsPerCat + k].setType (FancyShape.LABELSBOTTOM);
516               }
517             }
518             if (i + 1 == numDots) break;
519             minX = maxX;
520             if (i + 2 == numDots &&
521               getLabelsAxisTicksAlignment() == this.BETWEEN) {
522               maxX = getSpaceSizeLocation (MIN).x + getSpaceSize (MIN).width;
523             }
524             else maxX = xTicks[i + 1].x + tickHalfWidth;
525             ++i;
526           } while (true);
527         }
528       }
529
530       if ((numCats * numCompsPerCat) < 2 || linesThickness < 1) lines = new FancyLine[0];
531       else {
532         lines = new FancyLine[numSets];
533         int y = getSpaceSizeLocation (MIN).y + getSpaceSize(MIN).height, x;
534         for (int i = 0; i < numSets; ++i) {
535           lines[i] = new FancyLine();
536           lines[i].setThickness (linesThickness);
537           lines[i].setFillArea (getLinesFillInterior());
538           lines[i].setColor (getLineColors()[i]);
539           lines[i].setOutlineExistence (getOutlineComponents());
540           lines[i].setOutlineColor (getOutlineComponentsColor());
541           lines[i].setLightSource (getComponentsLightSource());
542           lines[i].setWarningRegions (getWarningRegions());
543           lines[i].setType (FancyShape.LABELSBOTTOM);
544         }
545         Point firstPoints[] = new Point[numSets];
546         float minX, maxX, tickHalfWidth;
547         minX = getSpaceSizeLocation (MIN).x;
548         tickHalfWidth = xTicks[0].width / 2f;
549         maxX = xTicks[0].x + tickHalfWidth;
550         float catDelta =
551           getSpaceSize (MIN).width / (float)(numLinePoints * numCompsPerCat);
552         int i = 0;
553         do {
554           if (getLabelsAxisTicksAlignment() == BETWEEN) {
555             x = (int)(minX + Math.floor((maxX - minX - setsThickness) / 2f));
556           }
557           else x = (int)Math.floor (maxX - setsThickness / 2f);
558           x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
559           for (int k = 0; k < numCompsPerCat; ++k) {
560             for (int j = 0; j < numSets; ++j) {
561               int compX = x + j * componentsThickness + componentsThickness / 2;
562               Point thisPoint = new Point (
563                 (int)(compX + k * catDelta),
564                 (int)(y - graphLengths[j][i * numCompsPerCat + k]));
565               if (k == 0 && i == 0) {
566                 if (getLinesFillInterior()) {
567                   firstPoints[j] = new Point (
568                     (int)(compX + k * catDelta),
569                     y - getLinesFillInteriorBaseValue());
570                   lines[j].getLine().moveTo (firstPoints[j].x, firstPoints[j].y);
571                   lines[j].getLine().lineTo (thisPoint.x, thisPoint.y);
572                 }
573                 else {
574                   lines[j].getLine().moveTo (thisPoint.x, thisPoint.y);
575                 }
576               }
577               else {
578                 lines[j].getLine().lineTo (thisPoint.x, thisPoint.y);
579                 if (getLinesFillInterior() &&
580                   k == numCompsPerCat - 1 && i == numLinePoints - 1) {
581                     lines[j].getLine().lineTo (thisPoint.x, firstPoints[j].y);
582                     lines[j].getLine().lineTo (firstPoints[j].x, firstPoints[j].y);
583                 }
584               }
585             }
586           }
587           if (i + 1 == numLinePoints) break;
588           minX = maxX;
589           if (i + 2 == numLinePoints &&
590               getLabelsAxisTicksAlignment() == this.BETWEEN) {
591             maxX = getSpaceSizeLocation (MIN).x + getSpaceSize (MIN).width;
592           }
593           else maxX = xTicks[i + 1].x + tickHalfWidth;
594           ++i;
595         } while (true);
596         for (int g = 0; g < numSets; ++g) {
597           lines[g].setClipBounds (boundsGraph);
598           Rectangle2D.Float bounds = (Rectangle2D.Float)lines[g].getLine().getBounds2D();
599           lines[g].setLightBounds (
600             getComponentsLightType() == COMPONENT ? bounds : boundsGraph);
601         }
602       }
603     }
604     else {
605       bars = new FancyBar[0][0];
606       dots = new FancyDot[0][0];
607       lines = new FancyLine[0];
608     }
609   }
610
611
612   private void updateAllowAlignment() {
613
614     int[][] graphLengths = getGraphValues();
615     int numBars = 0, numDots = 0, numLinePoints = 0, numGaps = 0;
616     int numSets = graphLengths.length;
617     Rectangle[] xTicks = getXTicks();
618     int numCompsPerCat = 0;
619     int numCats = 0;
620     if (numSets > 0) {
621       numCats = getLabelsAxisTicksAlignment() == BETWEEN ?
622         xTicks.length + 1 : xTicks.length;
623       numBars = getBarsExistence() ? numCats : 0;
624       numDots = getDotsExistence() ? numCats : 0;
625       numLinePoints = getLinesExistence() ? numCats : 0;
626       numGaps = getBetweenComponentsGapExistence() ? numCats : 0;
627       numCompsPerCat = numCats > 0 ? (int)(graphLengths[0].length / numCats) : 0;
628     }
629
630     if (numSets > 0 && numCats > 0 && numCompsPerCat > 0) {
631       float ratio = getRatio (WIDTH);
632       int availableThickness = getSpaceSize(MIN).width;
633
634       int betweenComponentsGapThickness = 0;
635       if (numGaps > 0) {
636         betweenComponentsGapThickness =
637           applyRatio (getBetweenComponentsGapThicknessModel(), ratio);
638         betweenComponentsGapThickness =
639           numGaps * betweenComponentsGapThickness <= availableThickness ?
640           betweenComponentsGapThickness : availableThickness / numGaps;
641         availableThickness -= numGaps * betweenComponentsGapThickness;
642       }
643
644       int barsThickness = 0;
645       if (numBars > 0) {
646         barsThickness = applyRatio (getBarsThicknessModel(), ratio);
647         if (numBars * numCompsPerCat * barsThickness <= availableThickness) {
648           float leftover =
649             (availableThickness - barsThickness *
650             (numBars * numCompsPerCat)) /
651             (numBars * numCompsPerCat);
652           barsThickness =
653             barsThickness + (int)(getBarsExcessSpaceFeedbackRatio() * leftover);
654         }
655         else {
656           float numOverlapBarsPerCat =
657             (1 + (1 - getBarsWithinCategoryOverlapRatio()) *
658             (numCompsPerCat - 1));
659           if (numBars * numOverlapBarsPerCat * barsThickness >
660             availableThickness) {
661             barsThickness =
662               (int)(availableThickness / (numBars * numOverlapBarsPerCat));
663           }
664         }
665       }
666       int dotsThickness = 0;
667       if (numDots > 0) {
668         dotsThickness = applyRatio (getDotsThicknessModel(), getRatio (LESSER));
669         if (numDots * numCompsPerCat * dotsThickness <=
670           availableThickness) {
671           float leftover =
672             (availableThickness - dotsThickness *
673             (numDots * numCompsPerCat)) /
674             (numDots * numCompsPerCat);
675           dotsThickness =
676             dotsThickness + (int)(getDotsExcessSpaceFeedbackRatio() * leftover);
677         }
678         else {
679           float numOverlapDotsPerCat =
680             (1 + (1 - getDotsWithinCategoryOverlapRatio()) *
681             (numCompsPerCat - 1));
682           if (numDots * numOverlapDotsPerCat * dotsThickness >
683             availableThickness) {
684             dotsThickness =
685               (int)(availableThickness / (numDots * numOverlapDotsPerCat));
686           }
687         }
688       }
689       int linesThickness = 0;
690       if (numLinePoints > 0) {
691         linesThickness =
692           applyRatio (getLinesThicknessModel(), getRatio (LESSER));
693         if (numLinePoints * numCompsPerCat * linesThickness <=
694           availableThickness) {
695           float leftover =
696             (availableThickness - linesThickness *
697             (numLinePoints * numCompsPerCat)) /
698             (numLinePoints * numCompsPerCat);
699           linesThickness =
700             linesThickness + (int)(getLinesExcessSpaceFeedbackRatio() * leftover);
701         }
702         else {
703           float numOverlapLinesPerCat =
704             (1 + (1 - getLinesWithinCategoryOverlapRatio()) *
705             (numCompsPerCat - 1));
706           if (numLinePoints * numOverlapLinesPerCat * linesThickness >
707             availableThickness) {
708             linesThickness =
709               (int)(availableThickness / (numLinePoints * numOverlapLinesPerCat));
710           }
711         }
712       }
713
714       Rectangle2D.Float boundsGraph = new Rectangle2D.Float (
715         getSpaceSizeLocation (MIN).x, getSpaceSizeLocation (MIN).y,
716         getSpaceSize (MIN).width, getSpaceSize (MIN).height);
717
718       bars = new FancyBar[numSets][numBars * numCompsPerCat];
719       int[][] barLowValues = getBarLowValues();
720       if (numBars > 0) {
721         int y = getSpaceSizeLocation (MIN).y + getSpaceSize(MIN).height, x;
722         float catDelta =
723           getSpaceSize (MIN).width / (float)(numBars * numCompsPerCat);
724         if (numBars == 1) {
725           x = getSpaceSizeLocation (MIN).x +
726             (getSpaceSize(MIN).width - barsThickness) / 2;
727           x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
728           for (int k = 0; k < numCompsPerCat; ++k) {
729             for (int j = 0; j < numSets; ++j) {
730               bars[j][k] = new FancyBar();
731               bars[j][k].setDataSign (getDataSign());
732               bars[j][k].setBaseValue (y - getLinesFillInteriorBaseValue());
733               if (graphLengths[j][k] > barLowValues[j][k]) {
734                 bars[j][k].setBounds (new Rectangle2D.Float (
735                   x + k * catDelta, y - graphLengths[j][k],
736                   barsThickness, graphLengths[j][k] - barLowValues[j][k]));
737               }
738               else {
739                 bars[j][k].setBounds (new Rectangle2D.Float (
740                   x + k * catDelta, y - barLowValues[j][k],
741                   barsThickness, barLowValues[j][k] - graphLengths[j][k]));
742               }
743               Rectangle2D.Float clipBounds = new Rectangle2D.Float (
744                 boundsGraph.x, bars[j][k].getBounds().y,
745                 boundsGraph.width, bars[j][k].getBounds().height);
746               bars[j][k].setClipBounds (clipBounds);
747               bars[j][k].setLightBounds (
748                 getComponentsLightType() == COMPONENT ? bars[j][k].getBounds() : boundsGraph);
749               bars[j][k].setLightSource (getComponentsLightSource());
750               bars[j][k].setColor (!getComponentsColoringByCat() ?
751                 getBarColors()[j] : getComponentsColorsByCat()[0]);
752               bars[j][k].setOutlineExistence (getOutlineComponents());
753               bars[j][k].setOutlineColor (getOutlineComponentsColor());
754               bars[j][k].setArcw (getBarRoundingRatio() * barsThickness);
755               bars[j][k].setArch (getBarRoundingRatio() * barsThickness);
756               bars[j][k].setWarningRegions (getWarningRegions());
757               bars[j][k].setType (FancyShape.LABELSBOTTOM);
758               bars[j][k].setGraphBounds (boundsGraph);
759             }
760           }
761         }
762         else {
763           float minX, maxX, tickHalfWidth;
764           minX = getSpaceSizeLocation (MIN).x;
765           tickHalfWidth = xTicks[0].width / 2f;
766           maxX = xTicks[0].x + tickHalfWidth;
767           int i = 0;
768           do {
769             if (getLabelsAxisTicksAlignment() == BETWEEN) {
770               x = (int)(minX + Math.floor((maxX - minX - barsThickness) / 2f));
771             }
772             else x = (int)Math.floor (maxX - barsThickness / 2f);
773             x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
774             for (int k = 0; k < numCompsPerCat; ++k) {
775               for (int j = 0; j < numSets; ++j) {
776                 bars[j][i * numCompsPerCat + k] = new FancyBar();
777                 bars[j][i * numCompsPerCat + k].setDataSign (getDataSign());
778                 bars[j][i * numCompsPerCat + k].setBaseValue (y - getLinesFillInteriorBaseValue());
779                 if (graphLengths[j][i * numCompsPerCat + k] >
780                   barLowValues[j][i * numCompsPerCat + k]) {
781                   bars[j][i * numCompsPerCat + k].setBounds (new Rectangle2D.Float (
782                     x + k * catDelta, y - graphLengths[j][i * numCompsPerCat + k],
783                     barsThickness, graphLengths[j][i * numCompsPerCat + k] -
784                     barLowValues[j][i * numCompsPerCat + k]));
785                 }
786                 else {
787                   bars[j][i * numCompsPerCat + k].setBounds (new Rectangle2D.Float (
788                     (int)(x + k * catDelta),
789                     y - barLowValues[j][i * numCompsPerCat + k],
790                     barsThickness, barLowValues[j][i * numCompsPerCat + k] -
791                     graphLengths[j][i * numCompsPerCat + k]));
792                 }
793                 Rectangle2D.Float clipBounds = new Rectangle2D.Float (
794                   boundsGraph.x, bars[j][i * numCompsPerCat + k].getBounds().y,
795                   boundsGraph.width, bars[j][i * numCompsPerCat + k].getBounds().height);
796                 bars[j][i * numCompsPerCat + k].setClipBounds (clipBounds);
797                 bars[j][i * numCompsPerCat + k].setLightBounds (
798                   getComponentsLightType() == COMPONENT ?
799                   bars[j][i * numCompsPerCat + k].getBounds() : boundsGraph);
800                 bars[j][i * numCompsPerCat + k].setLightSource (getComponentsLightSource());
801                 bars[j][i * numCompsPerCat + k].setColor (!getComponentsColoringByCat() ?
802                   getBarColors()[j] : getComponentsColorsByCat()[i]);
803                 bars[j][i * numCompsPerCat + k].setOutlineExistence (getOutlineComponents());
804                 bars[j][i * numCompsPerCat + k].setOutlineColor (getOutlineComponentsColor());
805                 bars[j][i * numCompsPerCat + k].setArcw (getBarRoundingRatio() * barsThickness);
806                 bars[j][i * numCompsPerCat + k].setArch (getBarRoundingRatio() * barsThickness);
807                 bars[j][i * numCompsPerCat + k].setWarningRegions (getWarningRegions());
808                 bars[j][i * numCompsPerCat + k].setType (FancyShape.LABELSBOTTOM);
809                 bars[j][i * numCompsPerCat + k].setGraphBounds (boundsGraph);
810               }
811             }
812             if (i + 1 == numBars) break;
813             minX = maxX;
814             if (i + 2 == numBars &&
815               getLabelsAxisTicksAlignment() == BETWEEN) {
816               maxX = getSpaceSizeLocation (MIN).x + getSpaceSize (MIN).width;
817             }
818             else maxX = xTicks[i + 1].x + tickHalfWidth;
819             ++i;
820           } while (true);
821         }
822       }
823
824       dots = new FancyDot[numSets][numDots * numCompsPerCat];
825       if (numDots > 0) {
826         int y = getSpaceSizeLocation (MIN).y + getSpaceSize(MIN).height -
827           dotsThickness / 2, x;
828         float catDelta =
829           getSpaceSize (MIN).width / (float)(numDots * numCompsPerCat);
830         if (numDots == 1) {
831           x = getSpaceSizeLocation (MIN).x +
832             (getSpaceSize(MIN).width - dotsThickness) / 2;
833           x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
834           for (int k = 0; k < numCompsPerCat; ++k) {
835             for (int j = 0; j < numSets; ++j) {
836               dots[j][k] = new FancyDot();
837               dots[j][k].setBounds (new Rectangle2D.Float (
838                 x + k * catDelta, y - graphLengths[j][k], dotsThickness, dotsThickness));
839               dots[j][k].setClipBounds (dots[j][k].getBounds());
840               dots[j][k].setColor (!getComponentsColoringByCat() ?
841                 getDotColors()[j] : getComponentsColorsByCat()[0]);
842               dots[j][k].setOutlineExistence (getOutlineComponents());
843               dots[j][k].setOutlineColor (getOutlineComponentsColor());
844               dots[j][k].setLightBounds (
845                 getComponentsLightType() == COMPONENT ? dots[j][k].getBounds() : boundsGraph);
846               dots[j][k].setLightSource (getComponentsLightSource());
847               dots[j][k].setWarningRegions (getWarningRegions());
848               dots[j][k].setType (FancyShape.LABELSBOTTOM);
849             }
850           }
851         }
852         else {
853           float minX, maxX, tickHalfWidth;
854           minX = getSpaceSizeLocation (MIN).x;
855           tickHalfWidth = xTicks[0].width / 2f;
856           maxX = xTicks[0].x + tickHalfWidth;
857           int i = 0;
858           do {
859             if (getLabelsAxisTicksAlignment() == this.BETWEEN) {
860               x = (int)(minX + Math.floor((maxX - minX - dotsThickness) / 2f));
861             }
862             else x = (int)Math.floor (maxX - dotsThickness / 2f);
863             x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
864             for (int k = 0; k < numCompsPerCat; ++k) {
865               for (int j = 0; j < numSets; ++j) {
866                 dots[j][i * numCompsPerCat + k] = new FancyDot();
867                 dots[j][i * numCompsPerCat + k].setBounds (new Rectangle2D.Float (
868                   x + k * catDelta, y - graphLengths[j][i * numCompsPerCat + k],
869                   dotsThickness, dotsThickness));
870                 dots[j][i * numCompsPerCat + k].setClipBounds (
871                   dots[j][i * numCompsPerCat + k].getBounds());
872                 dots[j][i * numCompsPerCat + k].setColor (!getComponentsColoringByCat() ?
873                   getDotColors()[j] : getComponentsColorsByCat()[i]);
874                 dots[j][i * numCompsPerCat + k].setOutlineExistence (getOutlineComponents());
875                 dots[j][i * numCompsPerCat + k].setOutlineColor (getOutlineComponentsColor());
876                 dots[j][i * numCompsPerCat + k].setLightBounds (
877                   getComponentsLightType() == COMPONENT ?
878                   dots[j][i * numCompsPerCat + k].getBounds() : boundsGraph);
879                 dots[j][i * numCompsPerCat + k].setLightSource (getComponentsLightSource());
880                 dots[j][i * numCompsPerCat + k].setWarningRegions (getWarningRegions());
881                 dots[j][i * numCompsPerCat + k].setType (FancyShape.LABELSBOTTOM);
882               }
883             }
884             if (i + 1 == numDots) break;
885             minX = maxX;
886             if (i + 2 == numDots &&
887               getLabelsAxisTicksAlignment() == this.BETWEEN) {
888               maxX = getSpaceSizeLocation (MIN).x + getSpaceSize (MIN).width;
889             }
890             else maxX = xTicks[i + 1].x + tickHalfWidth;
891             ++i;
892           } while (true);
893         }
894       }
895
896
897       if ((numCats * numCompsPerCat) < 2 || linesThickness < 1) lines = new FancyLine[0];
898       else {
899         lines = new FancyLine[numSets];
900         int y = getSpaceSizeLocation (MIN).y + getSpaceSize(MIN).height, x;
901         for (int i = 0; i < numSets; ++i) {
902           lines[i] = new FancyLine();
903           lines[i].setThickness (linesThickness);
904           lines[i].setFillArea (getLinesFillInterior());
905           lines[i].setColor (getLineColors()[i]);
906           lines[i].setOutlineExistence (getOutlineComponents());
907           lines[i].setOutlineColor (getOutlineComponentsColor());
908           lines[i].setLightSource (getComponentsLightSource());
909           lines[i].setWarningRegions (getWarningRegions());
910           lines[i].setType (FancyShape.LABELSBOTTOM);
911         }
912         Point firstPoints[] = new Point[numSets];
913         float minX, maxX, tickHalfWidth;
914         minX = getSpaceSizeLocation (MIN).x;
915         tickHalfWidth = xTicks[0].width / 2f;
916         maxX = xTicks[0].x + tickHalfWidth;
917         float catDelta =
918           getSpaceSize (MIN).width / (float)(numLinePoints * numCompsPerCat);
919         int i = 0;
920         do {
921           if (getLabelsAxisTicksAlignment() == Area.BETWEEN) {
922             x = (int)(minX + Math.floor((maxX - minX) / 2f));
923           }
924           else {
925             x = (int)maxX;
926           }
927           x = (int)(x - ((numCompsPerCat - 1) / 2f) * catDelta);
928           for (int k = 0; k < numCompsPerCat; ++k) {
929             for (int j = 0; j < numSets; ++j) {
930               Point thisPoint = new Point (
931                 (int)(x + k * catDelta),
932                 (int)(y - graphLengths[j][i * numCompsPerCat + k]));
933               if (k == 0 && i == 0) {
934                 if (getLinesFillInterior()) {
935                   firstPoints[j] = new Point (
936                     (int)(x + k * catDelta),
937                     y - getLinesFillInteriorBaseValue());
938                   lines[j].getLine().moveTo (firstPoints[j].x, firstPoints[j].y);
939                   lines[j].getLine().lineTo (thisPoint.x, thisPoint.y);
940                 }
941                 else {
942                   lines[j].getLine().moveTo (thisPoint.x, thisPoint.y);
943                 }
944               }
945               else {
946                 lines[j].getLine().lineTo (thisPoint.x, thisPoint.y);
947                 if (getLinesFillInterior() &&
948                   k == numCompsPerCat - 1 && i == numLinePoints - 1) {
949                     lines[j].getLine().lineTo (thisPoint.x, firstPoints[j].y);
950                     lines[j].getLine().lineTo (firstPoints[j].x, firstPoints[j].y);
951                 }
952               }
953             }
954           }
955           if (i + 1 == numLinePoints) break;
956           minX = maxX;
957           if (i + 2 == numLinePoints &&
958             getLabelsAxisTicksAlignment() == this.BETWEEN) {
959             maxX = getSpaceSizeLocation (MIN).x + getSpaceSize (MIN).width;
960           }
961           else maxX = xTicks[i + 1].x + tickHalfWidth;
962           ++i;
963         } while (true);
964         for (int g = 0; g < numSets; ++g) {
965           lines[g].setClipBounds (boundsGraph);
966           Rectangle2D.Float bounds = (Rectangle2D.Float)lines[g].getLine().getBounds2D();
967           lines[g].setLightBounds (getComponentsLightType() == COMPONENT ? bounds : boundsGraph);
968         }
969       }
970     }
971     else {
972       bars = new FancyBar[0][0];
973       dots = new FancyDot[0][0];
974       lines = new FancyLine[0];
975     }
976   }
977 }
Popular Tags