001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   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   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: PieChart3D.java,v $
023   Revision 1.9  2004/05/05 22:24:14  markl
024   comment block updates
025
026   Revision 1.8  2003/11/07 19:17:27  markl
027   Added get/setDrawLabels() methods.
028
029   Revision 1.7  2003/01/19 09:33:21  markl
030   Javadoc & comment header updates.
031
032   Revision 1.6  2001/03/12 07:23:30  markl
033   Javadoc cleanup.
034
035   Revision 1.5  2000/10/17 20:43:10  markl
036   Fixes to catch null values.
037
038   Revision 1.4  2000/10/15 09:40:30  markl
039   Added javadoc and final API polishing.
040
041   Revision 1.3  2000/10/13 08:06:05  markl
042   Integration fixes, cleanup.
043
044   Revision 1.2  2000/10/13 02:04:20  markl
045   Added remaining classes, and integrated components with models.
046   ----------------------------------------------------------------------------
047*/
048
049/* Original paint logic lifted from 3D Pie Chart applet by Ciaran Treanor
050 * <ciaran@broadcom.ie>.
051 */
052
053package kiwi.ui.graph;
054
055import java.awt.*;
056import java.util.*;
057import javax.swing.*;
058
059/** A chart that renders the sum total of the values of each variable across
060 * data samples as a wedge in a circular "pie" whose whole represents the sum
061 * total of all values from all data samples. This type of chart is used to
062 * compare the cumulative contributions of values to the total.
063 *
064 * <p><center><img src="snapshot/PieChart3D.gif"><br>
065 * <i>An example PieChart3D.</i>
066 * </center>
067 *
068 * @author Ciaran Treanor
069 * @author Mark Lindner
070 */
071
072public class PieChart3D extends ChartView
073  {
074  private double aspectRatio = 2.5;
075  private int pieDepth = 5, pieWidth = 250, pieHeight;
076  private int cx, cy, rx, ry;
077  private boolean drawLabels = true;
078
079  /** Construct a new <code>PieChart3D</code> for the specified chart
080   * definition.
081   *
082   * @param chart The chart definition.
083   */
084  
085  public PieChart3D(Chart chart)
086    {
087    super(chart);
088
089    _init();
090    }
091
092  /*
093   */
094  
095  private void _init()
096    {
097    pieHeight = (int)(pieWidth / aspectRatio);
098    
099    rx = pieWidth / 2;
100    ry = pieHeight / 2;
101    
102    cx = rx + horizontalPad;
103    cy = ry + verticalPad;
104    }
105
106  /** Set the width of the pie.
107   *
108   * @param pieWidth The width, in pixels.
109   */
110
111  public void setPieWidth(int pieWidth)
112    {
113    this.pieWidth = pieWidth;
114
115    _init();
116    }
117
118  /** Get the width of the pie.
119   *
120   * @return The width, in pixels.
121   */
122  
123  public int getPieWidth()
124    {
125    return(pieWidth);
126    }
127
128  /** Set the depth of the pie.
129   *
130   * @param pieDepth The depth, in pixels.
131   */
132
133  public void setPieDepth(int pieDepth)
134    {
135    this.pieDepth = pieDepth;
136
137    _init();
138    }
139
140  /** Get the depth of the pie.
141   *
142   * @return The depth, in pixels.
143   */
144  
145  public int getPieDepth()
146    {
147    return(pieDepth);
148    }
149    
150  /** Paint the chart.
151   */
152
153  /** Specify whether labels will be drawn beside each slice in the pie
154   * chart.
155   *
156   * @since Kiwi 1.4.3
157   */
158
159  public void setDrawsLabels(boolean flag)
160    {
161    drawLabels = flag;
162    }
163
164  /** Determine whether labels will be drawn beside each slice in the pie
165   * chart.
166   *
167   * @since Kiwi 1.4.3
168   */
169
170  public boolean getDrawsLabels()
171    {
172    return(drawLabels);
173    }
174
175  /**
176   */
177  
178  protected void paintChart(Graphics gc)
179    {
180    int startAngle;
181    double angle;
182    Dimension d = getSize();
183    FontMetrics fm = getFontMetrics(getFont());
184
185    horizontalPad = (int)((d.getWidth() - pieWidth) / 2);
186    verticalPad = (int)((d.getHeight() - (pieHeight + pieDepth)) / 2);
187
188    cx = rx + horizontalPad;
189    cy = ry + verticalPad;
190
191    // precompute the slices
192
193    int sliceCount = chart.getValueCount();
194    double slices[] = new double[sliceCount];
195    Color colors[] = new Color[sliceCount];
196    double total = 0.0;
197    
198    for(int i = 0; i < sliceCount; i++)
199      {
200      ChartValue cv = (ChartValue)chart.getValueAt(i);
201      String var = cv.getName();
202      colors[i] = cv.getColor();
203
204      Enumeration e = model.getDataSamples();
205      while(e.hasMoreElements())
206        {
207        DataSample ds = (DataSample)e.nextElement();
208        Object o = ds.getValue(var);
209        double value = 0.0;
210        if((o != null) && (o instanceof Number))
211          value = ((Number)o).doubleValue();
212
213        slices[i] += value;
214        total += value;
215        }
216      }
217
218    // This is less than optimal, but we don't have a floodfill.
219    // Draw pieDepth-1 ovals in a darker color
220    
221    for(int x = pieDepth; x > 0; x--)
222      {
223      startAngle = -45;
224
225      
226      for(int i = 0; i < sliceCount; i++)
227        {
228        gc.setColor(colors[i].darker());
229        angle = Math.round(360 * (slices[i] / total));
230        gc.fillArc(horizontalPad, verticalPad + x, pieWidth,
231                   (int)(pieWidth / aspectRatio), startAngle, (int)angle);
232        startAngle += angle;
233        }
234      }
235
236    // Now draw the final (top) oval in the undarkened color
237    
238    startAngle = -45;
239    for(int i = 0; i < sliceCount; i++)
240      {
241      gc.setColor(colors[i]);
242      angle = Math.round(360 * (slices[i] / total));
243      gc.fillArc(horizontalPad, verticalPad, pieWidth,
244                 (int)(pieWidth / aspectRatio), startAngle, (int)angle);
245      startAngle += angle;
246      }
247
248    // add labels
249
250    if(drawLabels)
251      {
252      startAngle = -45;
253      gc.setColor(Color.black);
254    
255      double bisect, sx, sy;
256
257      for(int i = 0; i < sliceCount; i++)
258        {
259        angle = Math.round(360 * (slices[i] / total));
260        bisect = (startAngle + angle / 2.0) * Math.PI / 180.0;
261
262        sx = 1.2 * rx * Math.cos(bisect);
263        sy = 1.2 * ry * Math.sin(bisect);
264
265        String label = lm.formatDecimal(slices[i], precision)
266          + " (" + lm.formatPercentage((slices[i] / total), precision) + ")";
267
268        if(bisect < 0.0)
269          sy -= (fm.getAscent() + pieDepth);
270        else if(bisect < Math.PI / 2.0)
271          ;
272        else if(bisect < Math.PI)
273          sx -= fm.stringWidth(label);
274        else
275          {
276          /* bisect < 270 */
277          sx -= fm.stringWidth(label);
278          sy -= (fm.getAscent() + pieDepth);
279          }
280
281        gc.drawString(label, (int)(sx + cx), (int)(-sy + cy));
282      
283        startAngle += angle;
284        }
285      }
286    }
287
288  }
289
290/* end of source file */