KickJava   Java API By Example, From Geeks To Geeks.

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


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.Date JavaDoc;
30
31
32 /**
33  * A container for many variables and components relating to a pie area.
34  * A pie area is the area that contains only the pie of a pie chart. It doesn't
35  * have a legend.<br>
36  * <b>Features:</b><br>
37  * Allows for charting both floats and int values. Works fine even if all
38  * values are zero. Does not accept negative values. Outputs center points
39  * of sectors so that labels can be drawn into the sectors, touching these
40  * points. Outpouts how many sectors are in each quarter of the pie, so that
41  * appropriate placement of labels can be decided. The first datum in the
42  * data set will be the first sector beginning from the angle of 135 degrees and
43  * ending somewhere clockwise to it It will have the first color in the
44  * colors set. The remaining sectors are laid out similarly, clockwise.
45  */

46 final class PieArea extends Area {
47
48
49   private float[] dataset;
50   private Color[] colors;
51   private Paint[] paints;
52
53   private Arc2D.Float[] sectors;
54   private int numSectors;
55   private int[] numSectorsInQuarters;
56   private boolean outlineSectors;
57   private Color outlineSectorsColor;
58
59   private float sectorPointDepthRatio;
60   private Point[] pointsInSectors;
61   private float sectorGapPointRatio;
62   private Point[] pointsOutSectors;
63
64   private int piePrefSpaceSize;
65   private boolean customSizing;
66   private Dimension customSpaceSize;
67
68   private int lightSource;
69
70   private boolean needsUpdate;
71
72
73   /**
74    * Creates a pie area with the default values.
75    */

76   PieArea() {
77
78     setAutoSizes (false, false);
79     setAutoJustifys (false, false);
80     setBackgroundExistence (false);
81     setBorderExistence (false);
82     setGapExistence (true);
83     setGapThicknessModel (0);
84     setOutlineSectors (true);
85     setOutlineSectorsColor (Color.black);
86     setPiePrefSizeModel (100);
87     setSectorPointDepthRatio (.25f); //setSectorPointRatio
88
setSectorGapPointRatio (.25f);
89     setDataset (new float[0]);
90     setColors (new Color[0]);
91     setCustomSpaceSize (false, new Dimension());
92     setLightSource (TOP);
93     resetPieAreaModel (true);
94     needsUpdate = true;
95     numSectorsInQuarters = new int[4];
96   }
97
98
99   /**
100    * Sets from which direction the light is coming for shading of the pie sectors.
101    * @param source The direction of the light.
102    */

103   final void setLightSource (int source) {
104
105     needsUpdate = true;
106     lightSource = source;
107   }
108
109
110   /**
111    * Indicates whether the sectors should have a thin outline.
112    * @param outline If true, then the outline exists.
113    */

114   final void setOutlineSectors (boolean outline) {
115
116     needsUpdate = true;
117     outlineSectors = outline;
118   }
119
120
121   /**
122    * Indicates the color of the sectors outline if it exists.
123    * @param color The color for the outline.
124    */

125   final void setOutlineSectorsColor (Color color) {
126
127     needsUpdate = true;
128     outlineSectorsColor = color;
129   }
130
131
132   /**
133    * Determines how far into the sector the depth of the points from the
134    * getPointsInSectors() method are.
135    * @param ratio The ratio on the radius of the circle.
136    */

137   final void setSectorPointDepthRatio (float ratio) {
138     needsUpdate = true;
139     sectorPointDepthRatio = ratio;
140   }
141
142
143   /**
144    * Determines how far out of the sector the points from the
145    * getPointsOutSectors() method are.
146    * @param ratio The ratio on the radius of the circle.
147    */

148   final void setSectorGapPointRatio (float ratio) {
149     needsUpdate = true;
150     sectorGapPointRatio = ratio;
151   }
152
153
154   /**
155    * For input of the raw numbers to represent by the pie. Array element i
156    * is sector i, clockwise from degree 135.
157    * @param values The raw numbers to represent by the pie.
158    */

159   final void setDataset (float[] values) {
160
161     needsUpdate = true;
162     dataset = values;
163   }
164
165
166   /**
167    * For input of the color of each sector that represents a datum of the data
168    * set. Array element i is sector i, clockise from degree 135.
169    * @param colors The colors of the sectors.
170    */

171   final void setColors (Color[] colors) {
172
173     needsUpdate = true;
174     this.colors = colors;
175   }
176
177
178   /**
179    * Sets the model size of the pie. When initially drawn, the pie will be
180    * at least this size, if this size is less than the max space size of this
181    * area. After that and if magnificying, the pie size will be applied to the
182    * size ratios.
183    * @param size The model size of the pie.
184    */

185   final void setPiePrefSizeModel (int size) {
186
187     needsUpdate = true;
188     piePrefSpaceSize = size;
189   }
190
191
192   /**
193    * Sets the "minimum" size of the pie directly. If the minimum is too
194    * large, then the size will be the pie's maximum size.
195    * @param size The number to multiply to get the minimum size.
196    */

197   final void setCustomSpaceSize (boolean customize, Dimension size) {
198
199     needsUpdate = true;
200     customSizing = customize;
201     customSpaceSize = size;
202   }
203
204
205   /**
206    * Gets from which direction the light is coming for shading of the pie sectors.
207    * @return The direction of the light.
208    */

209   final int getLightSource() {
210     return lightSource;
211   }
212
213
214   /**
215    * Returns whether the sectors should have a thin outline.
216    * @return outline If true, then the outline exists.
217    */

218   final boolean getOutlineSectors() {
219
220     return outlineSectors;
221   }
222
223
224   /**
225    * Returns the color of the sectors outline if it exists.
226    * @return color The color for the outline.
227    */

228   final Color getOutlineSectorsColor() {
229
230     return outlineSectorsColor;
231   }
232
233
234   /**
235    * Returns the raw numbers to represent by the pie.
236    * @return The raw numbers to represent by the pie.
237    */

238   final float[] getDataset() {
239
240     return dataset;
241   }
242
243
244   /**
245    * Returns this property.
246    * @return The colors of the lines.
247    */

248   final Color[] getColors() {
249
250     return colors;
251   }
252
253
254   /**
255    * Returns this property.
256    * @return The number of sectors in this pie.
257    */

258   final int getNumSectors () {
259
260     updatePieArea();
261     return numSectors;
262   }
263
264
265   /**
266    * Returns this property. This property allows figuring out where to place
267    * labels if one wants to label this pie, more than is provided by the legend
268    * in PieChartArea. One can use a
269    * HorizontalTextListArea for the TOP and BOTTOM labels; and a
270    * VerticalTextListArea for the LEFT and RIGHT labels.
271    * This information helps them figure out how many and which labels belong in
272    * each text list.
273    * @return The number of sectors in the quarters. Actually, the number of
274    * center points of sectors in this quarter. Quarters are defined as follows:
275    * TOP = 135 to 45, LEFT = 45 to 315, BOTTOM = 315 to 225, RIGHT = 225 to 135.
276    */

277   final int[] getNumSectorsInQuarters() {
278
279     updatePieArea();
280     return numSectorsInQuarters;
281   }
282
283
284   /**
285    * Returns how far into the sector the depth of the points from the
286    * getPointsInSectors() method are.
287    * @return The ratio on the radius of the circle.
288    */

289   final float getSectorPointDepthRatio() {
290
291     return sectorPointDepthRatio;
292   }
293
294
295   /**
296    * Returns a point in each sector at the depth specified by
297    * setSectorsPointDepthRatio(float).
298    * @return Point[] The points inside the sectors.
299    */

300   final Point[] getPointsInSectors() {
301
302     updatePieArea();
303     return pointsInSectors;
304   }
305
306
307   /**
308    * Returns a point in out of each sector at the location indicated by
309    * setSectorsPointExternalRatio(float).
310    * @return Point[] The points outside the sectors.
311    */

312   final Point[] getPointsOutSectors() {
313     updatePieArea();
314     return pointsOutSectors;
315   }
316
317
318   /**
319    * Resets the model for this class. The model is used for shrinking and
320    * growing of its components based on the maximum size of this class. If this
321    * method is called, then the next time the maximum size is set, this classes
322    * model maximum size will be made equal to the new maximum size. Effectively
323    * what this does is ensure that whenever this objects maximum size is equal
324    * to the one given, then all of the components will take on their default
325    * model sizes. Note: This is only useful when auto model max sizing is
326    * disabled.
327    * @param reset True causes the max model size to be set upon the next max
328    * sizing.
329    */

330   final void resetPieAreaModel (boolean reset) {
331
332     needsUpdate = true;
333     resetAreaModel (reset);
334   }
335
336
337   /**
338    * Indicates whether some property of this class has changed.
339    * @return True if some property has changed.
340    */

341   final boolean getPieAreaNeedsUpdate() {
342
343     return (needsUpdate || getAreaNeedsUpdate());
344   }
345
346
347   /**
348    * Updates this parent's variables, and this' variables.
349    */

350   final void updatePieArea () {
351
352     if (getPieAreaNeedsUpdate()) {
353       updateArea();
354       update();
355     }
356     needsUpdate = false;
357   }
358
359
360   /**
361    * Paints all the components of this class. First all variables are updated.
362    * @param g2D The graphics context for calculations and painting.
363    */

364   final void paintComponent (Graphics2D g2D) {
365
366     updatePieArea();
367     super.paintComponent (g2D);
368
369     for (int i = 0; i < numSectors; ++i) {
370       g2D.setPaint (paints[i]);
371       g2D.fill (sectors[i]);
372     }
373
374     if (outlineSectors) {
375       for (int i = 0; i < numSectors; ++i) {
376           g2D.setColor (outlineSectorsColor);
377           g2D.draw (sectors[i]);
378       }
379     }
380   }
381
382
383   private void update() {
384
385     if (!getAutoSize (MIN) && !customSizing) {
386       piePrefSpaceSize = applyRatio (piePrefSpaceSize, getRatio (LESSER));
387       int tempPrefSize = piePrefSpaceSize < getSpaceSize (MAX).width ?
388         piePrefSpaceSize : getSpaceSize (MAX).width;
389       tempPrefSize = piePrefSpaceSize < getSpaceSize (MAX).height ?
390         piePrefSpaceSize : getSpaceSize (MAX).height;
391       setSpaceSize (MIN, new Dimension (tempPrefSize, tempPrefSize));
392     }
393     else if (!getAutoSize (MIN) && customSizing) {
394       setSpaceSize (MIN, customSpaceSize);
395     }
396
397     int width = getSpaceSize (MIN).width;
398     int height = getSpaceSize (MIN).height;
399     int minSpaceSizeSide = width < height ? width : height;
400
401     Point location = getSpaceSizeLocation (MIN);
402     int locationX = location.x + (int)((width - minSpaceSizeSide) / 2f);
403     int locationY = location.y + (int)((height - minSpaceSizeSide) / 2f);
404
405     numSectors = dataset.length;
406     sectors = new Arc2D.Float[numSectors];
407
408     float total = 0f;
409     for (int i = 0; i < numSectors; ++i) {
410       total = total + dataset[i];
411     }
412
413     if (total != 0f) {
414       float end = 135f;
415       float begin = 135f;
416       float extent = 0f;
417       for (int i = 0; i < (numSectors - 1); ++i) {
418         extent = (dataset[i] / total) * 360f;
419         begin = end - extent;
420         sectors[i] = new Arc2D.Float (locationX, locationY,
421           minSpaceSizeSide, minSpaceSizeSide, begin, extent, Arc2D.PIE);
422         end = begin;
423       }
424       extent = (dataset[numSectors - 1] / total) * 360f;
425       begin = 135f;
426       sectors[numSectors - 1] = new Arc2D.Float (locationX, locationY,
427         minSpaceSizeSide, minSpaceSizeSide, begin, extent, Arc2D.PIE);
428     }
429     else if (numSectors != 0) {
430       float end = 135f;
431       float begin = 135f;
432       float extent = (1f / numSectors) * 360f;
433       for (int i = 0; i < (numSectors - 1); ++i) {
434         begin = end - extent;
435         sectors[i] = new Arc2D.Float (locationX, locationY,
436           minSpaceSizeSide, minSpaceSizeSide, begin, extent, Arc2D.PIE);
437         end = begin;
438       }
439       begin = 135f;
440       sectors[numSectors - 1] = new Arc2D.Float (locationX, locationY,
441         minSpaceSizeSide, minSpaceSizeSide, begin, extent, Arc2D.PIE);
442     }
443
444     pointsInSectors = new Point[numSectors];
445     float radius = minSpaceSizeSide / 2f;
446     float centerX = locationX + radius;
447     float centerY = locationY + radius;
448     for (int i = 0; i < numSectors; ++i) {
449       float begin = sectors[i].start;
450       float extent = sectors[i].extent;
451       float theta = begin + extent / 2f;
452       float offsetX = (float)((1f - sectorPointDepthRatio) * radius *
453         Math.cos (Math.toRadians (theta)));
454       float offsetY = (float)((1f - sectorPointDepthRatio) * radius *
455         Math.sin (Math.toRadians (theta)));
456       float x = centerX + offsetX;
457       float y = centerY - offsetY;
458       pointsInSectors[i] = new Point (Math.round (x), Math.round (y));
459     }
460
461     int gapThickness = getGapThickness();
462     pointsOutSectors = new Point[numSectors];
463     float outOffset = sectorGapPointRatio * gapThickness;
464     for (int i = 0; i < numSectors; ++i) {
465
466       float begin = sectors[i].start;
467       float extent = sectors[i].extent;
468       float theta = begin + extent / 2f;
469       float offsetX = (float)((radius + outOffset) *
470         Math.cos (Math.toRadians(theta)));
471       float offsetY = (float)((radius + outOffset) *
472         Math.sin (Math.toRadians(theta)));
473       float x = centerX + offsetX;
474       float y = centerY - offsetY;
475       pointsOutSectors[i] = new Point (Math.round (x), Math.round (y));
476     }
477
478     numSectorsInQuarters[TOP] = 0;
479     numSectorsInQuarters[RIGHT] = 0;
480     numSectorsInQuarters[BOTTOM] = 0;
481     numSectorsInQuarters[LEFT] = 0;
482
483     boolean topDone = false;
484
485     for (int i = 0; i < numSectors; ++i) {
486
487       float begin = sectors[i].start;
488       float extent = sectors[i].extent;
489       begin = (begin + 720) % 360;
490       extent = (extent + 720) % 360;
491       float angle = begin + (extent / 2);
492
493       if (angle <= 135 && angle > 45){
494         if (!topDone) ++numSectorsInQuarters[TOP];
495         else ++numSectorsInQuarters[LEFT];
496       }
497       else if (angle > 135 && angle <= 225){
498         ++numSectorsInQuarters[LEFT];
499         topDone = true;
500       }
501       else if (angle > 225 && angle <= 315){
502         ++numSectorsInQuarters[BOTTOM];
503         topDone = true;
504       }
505       else {
506         ++numSectorsInQuarters[RIGHT];
507         topDone = true;
508       }
509     }
510
511     paints = new Paint[colors.length];
512     for (int i = 0; i < paints.length; ++i) {
513       if (lightSource == TOP) {
514         paints[i] = new GradientPaint (locationX, locationY, colors[i].brighter(),
515           locationX, locationY + height, colors[i]);
516       }
517       else if (lightSource == BOTTOM) {
518         paints[i] = new GradientPaint (locationX, locationY, colors[i],
519           locationX, locationY + height, colors[i].brighter());
520       }
521       else if (lightSource == LEFT) {
522         paints[i] = new GradientPaint (locationX, locationY, colors[i].brighter(),
523           locationX + width, locationY, colors[i]);
524       }
525       else if (lightSource == RIGHT) {
526         paints[i] = new GradientPaint (locationX, locationY, colors[i],
527           locationX + width, locationY, colors[i].brighter());
528       }
529       else {
530         paints[i] = colors[i];
531       }
532     }
533   }
534 }
Popular Tags