KickJava   Java API By Example, From Geeks To Geeks.

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


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
30
31 /**
32  * A titled pie chart with legend exactly like PieChart2D with one difference.
33  * This chart does not keep the title near to it, when auto min sizing is
34  * disabled. In this scenario, the title would be near the top of the max size
35  * and the chart would be centered within the left over space. This is not
36  * desireable. PieChart2D forces the title as near as possible to the chart
37  * under unless explicitly set not to. PieChart2D uses this class. It
38  * creates one of these charts with min sizing enabled. That produces a
39  * chart that is where the title is forced near the chart, and the chart
40  * is centered in the middle of the area. But really, if any settings of the
41  * chart need to be changed they are generally changed here, or in one of its
42  * components available through a get method.
43  */

44 final class PieChartArea extends ChartArea {
45
46   private static final int LABEL = 0;
47   private static final int PIE = 1;
48
49   private PieInfoArea pieLabels;
50   private float pieToWidthRatio;
51   private float pieToHeightRatio;
52   private float pieLabelsToWidthRatio;
53   private float pieLabelsToHeightRatio;
54   private float[] dataset;
55   private Dimension prefSize;
56   private boolean needsUpdate;
57
58   private boolean linesExistence;
59   private GeneralPath[] lines;
60   private int linesThicknessModel;
61   private Color linesColor;
62   private BasicStroke linesStroke;
63
64   private boolean lineDotsExistence;
65   private Ellipse2D.Float[][] lineDots;
66   private int lineDotsThicknessModel;
67   private Color lineDotsColor;
68
69   private int tempX, tempY;
70
71
72   /**
73    * Creates a Pie Chart Area with default values.
74    */

75   PieChartArea() {
76
77     pieLabels = new PieInfoArea();
78     prefSize = new Dimension();
79     needsUpdate = true;
80
81     setPieLabelsToWidthRatio (.50f);
82     setPieLabelsToHeightRatio (.25f);
83
84     setLabelsLinesExistence (true);
85     setLabelsLinesThicknessModel (1);
86     setLabelsLinesColor (Color.black);
87     setLabelsLineDotsExistence (true);
88     setLabelsLineDotsThicknessModel (4);
89     setLabelsLineDotsColor (Color.black);
90     resetPieChartAreaModel (true);
91   }
92
93
94   /**
95    * The dataset to plot.
96    * @param set The set of data.
97    */

98   final void setDataset (float[] d) {
99
100     needsUpdate = true;
101     dataset = d;
102   }
103
104
105   /**
106    * Sets the existence of the dots on the lines between the pie sector labels
107    * and the pie sectors.
108    * @param existence If true, the dots will be painted.
109    */

110   final void setLabelsLineDotsExistence (boolean existence) {
111
112     lineDotsExistence = existence;
113   }
114
115
116   /**
117    * Returns the existence of the dots on the lines between the pie sector
118    * labels and the pie sectors.
119    * @return If true, the dots will be painted.
120    */

121   final boolean getLabelsLineDotsExistence() {
122
123     return lineDotsExistence;
124   }
125
126
127   /**
128    * Sets the model thickness of the dots on the lines between the pie sector
129    * labels and the pie sectors.
130    * @param thickness The model thickness of the dots.
131    */

132   final void setLabelsLineDotsThicknessModel (int thickness) {
133
134     needsUpdate = true;
135     lineDotsThicknessModel = thickness;
136   }
137
138
139   /**
140    * Returns the model thickness of the dots on the lines between the pie sector
141    * labels and the pie sectors.
142    * @return The model thickness of the dots.
143    */

144   final int getLabelsLineDotsThicknessModel() {
145
146     return lineDotsThicknessModel;
147   }
148
149
150   /**
151    * Sets the color of the dots on the lines between the pie sector labels and
152    * the pie sectors.
153    * @param color The color of the dots.
154    */

155   final void setLabelsLineDotsColor (Color color) {
156
157     needsUpdate = true;
158     lineDotsColor = color;
159   }
160
161
162   /**
163    * Returns the color of the dots on the lines between the pie sector labels
164    * and the pie sectors.
165    * @return The color of the dots.
166    */

167   final Color getLabelsLineDotsColor() {
168
169     return lineDotsColor;
170   }
171
172
173   /**
174    * Sets the existence of the lines between the pie sector labels and the pie
175    * sectors.
176    * @param existence If true, the lines will be painted.
177    */

178   final void setLabelsLinesExistence (boolean existence) {
179
180     linesExistence = existence;
181   }
182
183
184   /**
185    * Sets the model thickness of the lines between the pie sector labels and the
186    * pie sectors.
187    * @param thickness The model thickness of the lines.
188    */

189   final void setLabelsLinesThicknessModel (int thickness) {
190
191     needsUpdate = true;
192     linesThicknessModel = thickness;
193   }
194
195
196   /**
197    * Return the model thickness of the lines between the pie sector labels and
198    * the pie sectors.
199    * @return The model thickness of the lines.
200    */

201   final int getLabelsLinesThicknessModel() {
202
203     return linesThicknessModel;
204   }
205
206
207   /**
208    * Sets the color of the lines between the pie sector labels and the pie
209    * sectors.
210    * @param color The color of the lines.
211    */

212   final void setLabelsLinesColor (Color color) {
213
214     needsUpdate = true;
215     linesColor = color;
216   }
217
218
219   /**
220    * Returns the color of the lines between the pie sector labels and the pie
221    * sectors.
222    * @return The color of the lines.
223    */

224   final Color getLabelsLinesColor() {
225
226     return linesColor;
227   }
228
229
230   /**
231    * Sets the width to be shared by all the labels, beyond the width of the
232    * pie. For instance, if there are labels on the left and the right of the
233    * pie, then their max widths will be equal and will each be half of the width
234    * indicated by applying the ratio to the max width of the chart.
235    * @param ratio The ratio for indicating the max widths of the labels.
236    */

237   final void setPieLabelsToWidthRatio (float ratio) {
238
239     needsUpdate = true;
240     pieLabelsToWidthRatio = ratio;
241   }
242
243
244   /**
245    * Sets the height to be shared by all the labels, beyond the height of the
246    * pie. For instance, if there are lables on the top and the bottom of the
247    * pie, then their max heights will be equal and will each be half of the
248    * height indicated by applying the ratio to the max width of the chart.
249    * @param ratio The ratio for indicating the max heights of the labels.
250    */

251   final void setPieLabelsToHeightRatio (float ratio) {
252
253     needsUpdate = true;
254     pieLabelsToHeightRatio = ratio;
255   }
256
257
258   /**
259    * Returns the minimum size that the chart would need if it was to be redrawn,
260    * the "preferred" size. The preferred size is the minimum size which would
261    * need to be set as the maxmodel size of the chart, if the chart was to be
262    * redrawn (assuming magnification is disabled).
263    * @param g2D The graphics context for calculations and painting.
264    * @return The size of the minimum maxmodel for a redraw.
265    */

266   final Dimension getPrefSize (Graphics2D g2D) {
267
268     if (getPieChartAreaNeedsUpdate()) updatePieChartArea (g2D);
269     return prefSize;
270   }
271
272
273   /**
274    * Returns the pie labels component of this chart in order to allow
275    * customization of it.
276    * @return The pie labels area.
277    */

278   final PieInfoArea getPieInfoArea() {
279     return pieLabels;
280   }
281
282
283   /**
284    * Resets the model for this class. The model is used for shrinking and
285    * growing of its components based on the maximum size of this class. If this
286    * method is called, then the next time the maximum size is set, this classes
287    * model maximum size will be made equal to the new maximum size. Effectively
288    * what this does is ensure that whenever this objects maximum size is equal
289    * to the one given, then all of the components will take on their default
290    * model sizes. Note: This is only useful when auto model max sizing is
291    * disabled.
292    * @param reset True causes the max model to be reset upon next max sizing.
293    */

294   final void resetPieChartAreaModel (boolean reset) {
295
296     resetChartAreaModel (reset);
297     pieLabels.resetPieInfoAreaModel (reset);
298   }
299
300
301   /**
302    * Indicates whether some property of this class has changed.
303    * @return True if some property has changed.
304    */

305   final boolean getPieChartAreaNeedsUpdate() {
306
307     if (needsUpdate || getChartAreaNeedsUpdate() || pieLabels.getPieInfoAreaNeedsUpdate())
308       return true;
309     return false;
310   }
311
312
313   /**
314    * Updates this parent's variables, and this' variables.
315    * @param g2D The graphics context to use for calculations.
316    */

317   final void updatePieChartArea (Graphics2D g2D) {
318
319     if (getPieChartAreaNeedsUpdate()) {
320       updateChartArea (g2D);
321       update (g2D);
322       pieLabels.updatePieInfoArea (g2D);
323     }
324     needsUpdate = false;
325   }
326
327
328   /**
329    * Paints pie chart area components.
330    * @param g2D The graphics context to use for calculations.
331    */

332   final void paintComponent (Graphics2D g2D) {
333
334     updatePieChartArea (g2D);
335     super.paintComponent (g2D);
336     pieLabels.paintComponent (g2D);
337
338     if (pieLabels.getPieLabelsExistence() && linesExistence) {
339       g2D.setColor (linesColor);
340       g2D.setStroke (linesStroke);
341       for (int i = 0; i < dataset.length; ++i) {
342         g2D.draw (lines[i]);
343       }
344     }
345
346     if (pieLabels.getPieLabelsExistence() && lineDotsExistence) {
347       g2D.setColor (lineDotsColor);
348       for (int i = 0; i < dataset.length; ++i) {
349         g2D.fill (lineDots[i][LABEL]);
350         g2D.fill (lineDots[i][PIE]);
351       }
352     }
353   }
354
355
356   private void update (Graphics2D g2D) {
357
358     LegendArea legend = getLegend();
359
360     float widthRatio = getRatio (WIDTH);
361     float heightRatio = getRatio (HEIGHT);
362     pieLabels.setCustomRatio (WIDTH, true, widthRatio);
363     pieLabels.setCustomRatio (HEIGHT, true, heightRatio);
364     legend.setCustomRatio (WIDTH, true, widthRatio);
365     legend.setCustomRatio (HEIGHT, true, heightRatio);
366
367     pieLabels.setRawLabelsPrecision (getLabelsPrecisionNum());
368     pieLabels.setDatasetColors (getDatasetColors());
369     pieLabels.setDataset (dataset);
370
371     Rectangle maxBounds = getMaxEntitledSpaceBounds (g2D);
372
373     int betweenChartAndLegendGapThickness = 0;
374     int availableWidth = maxBounds.width;
375     if (getBetweenChartAndLegendGapExistence() && getLegendExistence()) {
376
377       betweenChartAndLegendGapThickness =
378         applyRatio (getBetweenChartAndLegendGapThicknessModel(), getRatio (WIDTH));
379       betweenChartAndLegendGapThickness =
380         betweenChartAndLegendGapThickness <= availableWidth ?
381         betweenChartAndLegendGapThickness : availableWidth;
382       availableWidth -= betweenChartAndLegendGapThickness;
383     }
384
385     int legendWidth = 0, legendHeight = 0;
386     float legendToWidthRatio = getLegendToWidthRatio();
387     float legendToHeightRatio = getLegendToHeightRatio();
388     if (getLegendExistence()) {
389       legendWidth = (int)(legendToWidthRatio * availableWidth);
390       legendHeight = (int)(legendToHeightRatio * maxBounds.height);
391     }
392     legend.setSize (MAX, new Dimension (legendWidth, legendHeight));
393     legend.updateLegendArea (g2D);
394     legendWidth = legend.getSize (MIN).width;
395     legendHeight = legend.getSize (MIN).height;
396
397     int pieLabelsWidth = 0, pieLabelsHeight = 0;
398     pieLabelsWidth = (int)(pieLabelsToWidthRatio * availableWidth);
399     pieLabelsHeight = (int)(pieLabelsToHeightRatio * maxBounds.height);
400     pieLabels.setSize (MAX, new Dimension (pieLabelsWidth, pieLabelsHeight));
401     pieLabels.setCustomSize (false, new Dimension());
402     pieLabels.updatePieInfoArea (g2D);
403     pieLabelsWidth = pieLabels.getSize (MIN).width;
404     pieLabelsHeight = pieLabels.getSize (MIN).height;
405
406     int width = 0, height = 0;
407     width = pieLabelsWidth + betweenChartAndLegendGapThickness + legendWidth;
408     height = pieLabelsHeight > legendHeight ? pieLabelsHeight : legendHeight;
409
410     if (getAutoSetLayoutRatios()) {
411
412       width -= betweenChartAndLegendGapThickness;
413
414       pieLabelsToWidthRatio = width > 0 ? pieLabelsWidth / (float)width : 0f;
415       pieLabelsToWidthRatio = pieLabelsToWidthRatio < 1f ? pieLabelsToWidthRatio : 1f;
416       pieLabelsToHeightRatio = height > 0 ? pieLabelsHeight / (float)height : 0f;
417       pieLabelsToHeightRatio = pieLabelsToHeightRatio < 1f ? pieLabelsToHeightRatio : 1f;
418
419       legendToWidthRatio = legendToWidthRatio != 0f ? 1f - pieLabelsToWidthRatio : 0f;
420       legendToHeightRatio = legendToHeightRatio != 0f ? 1f : 0f;
421
422       if (pieLabelsToWidthRatio <= 0f || pieLabelsToHeightRatio <= 0f) {
423         pieLabelsToWidthRatio = pieLabelsToHeightRatio = 0f;
424       }
425
426       if (legendToWidthRatio <= 0f || legendToHeightRatio <= 0f) {
427         legendToWidthRatio = legendToHeightRatio = 0f;
428       }
429
430       setPieLabelsToWidthRatio (pieLabelsToWidthRatio);
431       setPieLabelsToHeightRatio (pieLabelsToHeightRatio);
432
433       setLegendToWidthRatio (legendToWidthRatio);
434       setLegendToHeightRatio (legendToHeightRatio);
435
436       setAutoSetLayoutRatios (false);
437
438       width += betweenChartAndLegendGapThickness;
439     }
440
441     Dimension titleSize = getTitleSize (MIN, g2D);
442     int titleGap = getBetweenTitleAndSpaceGapThickness (g2D);
443     int prefWidth1 = width + 2 * getOffsetThickness();
444     int prefWidth2 = titleSize.width + 2 * getOffsetThickness();
445     int prefWidth = prefWidth1 > prefWidth2 ? prefWidth1 : prefWidth2;
446     int prefHeight = height + 2 * getOffsetThickness() + titleSize.height + titleGap;
447     prefSize = new Dimension ((int)(1.3f * prefWidth), (int)(1.3f * prefHeight));
448
449     if (getAutoSize (MIN)) {
450
451       int deficientWidth = maxBounds.width - width;
452       int deficientHeight = maxBounds.height - pieLabelsHeight;
453       int deficient = deficientWidth < deficientHeight ? deficientWidth : deficientHeight;
454
455       pieLabels.setCustomSize (
456         true, new Dimension (pieLabelsWidth + deficientWidth, pieLabelsHeight + deficientHeight));
457
458       pieLabels.updatePieInfoArea (g2D);
459
460       pieLabelsWidth = pieLabels.getSize (MIN).width;
461       pieLabelsHeight = pieLabels.getSize (MIN).height;
462
463       width = pieLabelsWidth + betweenChartAndLegendGapThickness + legendWidth;
464       height = pieLabelsHeight > legendHeight ? pieLabelsHeight : legendHeight;
465     }
466
467     Rectangle minBounds = new Rectangle();
468     minBounds.setSize (width, height);
469
470     if (!getAutoSize (MIN)) {
471
472       int minWidth = titleSize.width > minBounds.width ? titleSize.width : minBounds.width;
473       int minHeight;
474       if (titleSize.height > 0 && minBounds.height > 0) {
475         minHeight = titleSize.height + titleGap + minBounds.height;
476       }
477       else minHeight = titleSize.height + minBounds.height;
478       setSpaceSize (MIN, new Dimension (minWidth, minHeight));
479     }
480
481     int x = maxBounds.x + (maxBounds.width - minBounds.width) / 2;
482     int y = maxBounds.y + (maxBounds.height - minBounds.height) / 2;
483     minBounds.setLocation (x, y);
484
485     if (getTitleSqueeze()) {
486       int titleX = maxBounds.x + (maxBounds.width - titleSize.width) / 2;
487       int titleY = minBounds.y - titleSize.height - titleGap;
488       setTitleLocation (new Point (titleX, titleY));
489     }
490
491     int legendX, legendY, pieLabelsX, pieLabelsY;
492     pieLabelsX = minBounds.x;
493     legendX = minBounds.x + minBounds.width - legendWidth + legend.getOffsetThickness();
494
495     if (pieLabelsHeight > legendHeight) {
496       pieLabelsY = minBounds.y;
497       legendY = pieLabelsY + (pieLabelsHeight - legend.getSpaceSize (MIN).height) / 2;
498     }
499     else {
500       legendY = minBounds.y + legend.getOffsetThickness();
501       pieLabelsY = legendY + (legendHeight - pieLabels.getSpaceSize (MIN).height) / 2;
502     }
503
504     pieLabels.setSpaceSizeLocation (MIN, new Point (pieLabelsX, pieLabelsY));
505     pieLabels.updatePieInfoArea (g2D);
506
507     legend.setSpaceSizeLocation (MIN, new Point (legendX, legendY));
508     legend.updateLegendArea (g2D);
509
510     PieArea pie = pieLabels.getPieArea();
511
512     Point[] linesLabel = pieLabels.getPointsNearLabels (g2D);
513     Point[] linesGap = pie.getPointsOutSectors();
514     Point[] linesSector = pie.getPointsInSectors();
515     lines = new GeneralPath[dataset.length];
516     for (int i = 0; i < dataset.length; ++i) {
517
518       lines[i] = new GeneralPath (GeneralPath.WIND_EVEN_ODD, 3);
519       lines[i].moveTo (linesSector[i].x, linesSector[i].y);
520       lines[i].lineTo (linesGap[i].x, linesGap[i].y);
521       lines[i].lineTo (linesLabel[i].x, linesLabel[i].y);
522     }
523     int tempLinesThicknessModel =
524       linesThicknessModel > 2 * pieLabels.getLabelsPointsGapThicknessModel() ?
525       2 * pieLabels.getLabelsPointsGapThicknessModel() : linesThicknessModel;
526     tempLinesThicknessModel =
527       tempLinesThicknessModel > 2 * pie.getGapThicknessModel() ?
528       2 * pie.getGapThicknessModel() : tempLinesThicknessModel;
529     int linesThickness =
530       applyRatio (tempLinesThicknessModel, getRatio (LESSER));
531     linesThickness = linesThickness > pie.getGapThickness() ?
532       pie.getGapThickness() : linesThickness;
533     float[] style = {10.0f, 0.0f};
534     linesStroke = new BasicStroke (
535       (float)linesThickness,
536       BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f, style, 0.0f);
537
538     lineDots = new Ellipse2D.Float[dataset.length][2];
539     int tempLineDotsThicknessModel =
540       lineDotsThicknessModel >
541       2 * pieLabels.getLabelsPointsGapThicknessModel() ?
542       2 * pieLabels.getLabelsPointsGapThicknessModel() : lineDotsThicknessModel;
543     tempLineDotsThicknessModel =
544       tempLineDotsThicknessModel > 2 * pie.getGapThicknessModel() ?
545       2 * pie.getGapThicknessModel() : tempLineDotsThicknessModel;
546     int lineDotsThickness =
547       applyRatio (tempLineDotsThicknessModel, getRatio (LESSER));
548     for (int i = 0; i < dataset.length; ++i) {
549
550       lineDots[i][PIE] = new Ellipse2D.Float (
551           linesSector[i].x - lineDotsThickness / 2f,
552           linesSector[i].y - lineDotsThickness / 2f,
553           lineDotsThickness, lineDotsThickness);
554       lineDots[i][LABEL] = new Ellipse2D.Float (
555           linesLabel[i].x - lineDotsThickness / 2f,
556           linesLabel[i].y - lineDotsThickness / 2f,
557           lineDotsThickness, lineDotsThickness);
558     }
559   }
560 }
Popular Tags