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 */