001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * --------------------- 028 * WaferMapRenderer.java 029 * --------------------- 030 * (C) Copyright 2003-2008, by Robert Redburn and Contributors. 031 * 032 * Original Author: Robert Redburn; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * Changes 036 * ------- 037 * 25-Nov-2003 : Version 1, contributed by Robert Redburn. Changes have been 038 * made to fit the JFreeChart coding style (DG); 039 * 20-Apr-2005 : Small update for changes to LegendItem class (DG); 040 * ------------- JFREECHART 1.0.x --------------------------------------------- 041 * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG); 042 * 043 */ 044 045 package org.jfree.chart.renderer; 046 047 import java.awt.Color; 048 import java.awt.Paint; 049 import java.awt.Shape; 050 import java.awt.Stroke; 051 import java.awt.geom.Rectangle2D; 052 import java.util.HashMap; 053 import java.util.HashSet; 054 import java.util.Iterator; 055 import java.util.Map; 056 import java.util.Set; 057 058 import org.jfree.chart.LegendItem; 059 import org.jfree.chart.LegendItemCollection; 060 import org.jfree.chart.plot.DrawingSupplier; 061 import org.jfree.chart.plot.WaferMapPlot; 062 import org.jfree.data.general.WaferMapDataset; 063 064 /** 065 * A renderer for wafer map plots. Provides color managment facilities. 066 */ 067 public class WaferMapRenderer extends AbstractRenderer { 068 069 /** paint index */ 070 private Map paintIndex; 071 072 /** plot */ 073 private WaferMapPlot plot; 074 075 /** paint limit */ 076 private int paintLimit; 077 078 /** default paint limit */ 079 private static final int DEFAULT_PAINT_LIMIT = 35; 080 081 /** default multivalue paint calculation */ 082 public static final int POSITION_INDEX = 0; 083 084 /** The default value index. */ 085 public static final int VALUE_INDEX = 1; 086 087 /** paint index method */ 088 private int paintIndexMethod; 089 090 /** 091 * Creates a new renderer. 092 */ 093 public WaferMapRenderer() { 094 this(null, null); 095 } 096 097 /** 098 * Creates a new renderer. 099 * 100 * @param paintLimit the paint limit. 101 * @param paintIndexMethod the paint index method. 102 */ 103 public WaferMapRenderer(int paintLimit, int paintIndexMethod) { 104 this(new Integer(paintLimit), new Integer(paintIndexMethod)); 105 } 106 107 /** 108 * Creates a new renderer. 109 * 110 * @param paintLimit the paint limit. 111 * @param paintIndexMethod the paint index method. 112 */ 113 public WaferMapRenderer(Integer paintLimit, Integer paintIndexMethod) { 114 115 super(); 116 this.paintIndex = new HashMap(); 117 118 if (paintLimit == null) { 119 this.paintLimit = DEFAULT_PAINT_LIMIT; 120 } 121 else { 122 this.paintLimit = paintLimit.intValue(); 123 } 124 125 this.paintIndexMethod = VALUE_INDEX; 126 if (paintIndexMethod != null) { 127 if (isMethodValid(paintIndexMethod.intValue())) { 128 this.paintIndexMethod = paintIndexMethod.intValue(); 129 } 130 } 131 } 132 133 /** 134 * Verifies that the passed paint index method is valid. 135 * 136 * @param method the method. 137 * 138 * @return <code>true</code> or </code>false</code>. 139 */ 140 private boolean isMethodValid(int method) { 141 switch (method) { 142 case POSITION_INDEX: return true; 143 case VALUE_INDEX: return true; 144 default: return false; 145 } 146 } 147 148 /** 149 * Returns the drawing supplier from the plot. 150 * 151 * @return The drawing supplier. 152 */ 153 public DrawingSupplier getDrawingSupplier() { 154 DrawingSupplier result = null; 155 WaferMapPlot p = getPlot(); 156 if (p != null) { 157 result = p.getDrawingSupplier(); 158 } 159 return result; 160 } 161 162 /** 163 * Returns the plot. 164 * 165 * @return The plot. 166 */ 167 public WaferMapPlot getPlot() { 168 return this.plot; 169 } 170 171 /** 172 * Sets the plot and build the paint index. 173 * 174 * @param plot the plot. 175 */ 176 public void setPlot(WaferMapPlot plot) { 177 this.plot = plot; 178 makePaintIndex(); 179 } 180 181 /** 182 * Returns the paint for a given chip value. 183 * 184 * @param value the value. 185 * 186 * @return The paint. 187 */ 188 public Paint getChipColor(Number value) { 189 return getSeriesPaint(getPaintIndex(value)); 190 } 191 192 /** 193 * Returns the paint index for a given chip value. 194 * 195 * @param value the value. 196 * 197 * @return The paint index. 198 */ 199 private int getPaintIndex(Number value) { 200 return ((Integer) this.paintIndex.get(value)).intValue(); 201 } 202 203 /** 204 * Builds a map of chip values to paint colors. 205 * paintlimit is the maximum allowed number of colors. 206 */ 207 private void makePaintIndex() { 208 if (this.plot == null) { 209 return; 210 } 211 WaferMapDataset data = this.plot.getDataset(); 212 Number dataMin = data.getMinValue(); 213 Number dataMax = data.getMaxValue(); 214 Set uniqueValues = data.getUniqueValues(); 215 if (uniqueValues.size() <= this.paintLimit) { 216 int count = 0; // assign a color for each unique value 217 for (Iterator i = uniqueValues.iterator(); i.hasNext();) { 218 this.paintIndex.put(i.next(), new Integer(count++)); 219 } 220 } 221 else { 222 // more values than paints so map 223 // multiple values to the same color 224 switch (this.paintIndexMethod) { 225 case POSITION_INDEX: 226 makePositionIndex(uniqueValues); 227 break; 228 case VALUE_INDEX: 229 makeValueIndex(dataMax, dataMin, uniqueValues); 230 break; 231 default: 232 break; 233 } 234 } 235 } 236 237 /** 238 * Builds the paintindex by assigning colors based on the number 239 * of unique values: totalvalues/totalcolors. 240 * 241 * @param uniqueValues the set of unique values. 242 */ 243 private void makePositionIndex(Set uniqueValues) { 244 int valuesPerColor = (int) Math.ceil( 245 (double) uniqueValues.size() / this.paintLimit 246 ); 247 int count = 0; // assign a color for each unique value 248 int paint = 0; 249 for (Iterator i = uniqueValues.iterator(); i.hasNext();) { 250 this.paintIndex.put(i.next(), new Integer(paint)); 251 if (++count % valuesPerColor == 0) { 252 paint++; 253 } 254 if (paint > this.paintLimit) { 255 paint = this.paintLimit; 256 } 257 } 258 } 259 260 /** 261 * Builds the paintindex by assigning colors evenly across the range 262 * of values: maxValue-minValue/totalcolors 263 * 264 * @param max the maximum value. 265 * @param min the minumum value. 266 * @param uniqueValues the unique values. 267 */ 268 private void makeValueIndex(Number max, Number min, Set uniqueValues) { 269 double valueRange = max.doubleValue() - min.doubleValue(); 270 double valueStep = valueRange / this.paintLimit; 271 int paint = 0; 272 double cutPoint = min.doubleValue() + valueStep; 273 for (Iterator i = uniqueValues.iterator(); i.hasNext();) { 274 Number value = (Number) i.next(); 275 while (value.doubleValue() > cutPoint) { 276 cutPoint += valueStep; 277 paint++; 278 if (paint > this.paintLimit) { 279 paint = this.paintLimit; 280 } 281 } 282 this.paintIndex.put(value, new Integer(paint)); 283 } 284 } 285 286 /** 287 * Builds the list of legend entries. called by getLegendItems in 288 * WaferMapPlot to populate the plot legend. 289 * 290 * @return The legend items. 291 */ 292 public LegendItemCollection getLegendCollection() { 293 LegendItemCollection result = new LegendItemCollection(); 294 if (this.paintIndex != null && this.paintIndex.size() > 0) { 295 if (this.paintIndex.size() <= this.paintLimit) { 296 for (Iterator i = this.paintIndex.entrySet().iterator(); 297 i.hasNext();) { 298 // in this case, every color has a unique value 299 Map.Entry entry = (Map.Entry) i.next(); 300 String label = entry.getKey().toString(); 301 String description = label; 302 Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); 303 Paint paint = lookupSeriesPaint( 304 ((Integer) entry.getValue()).intValue()); 305 Paint outlinePaint = Color.black; 306 Stroke outlineStroke = DEFAULT_STROKE; 307 308 result.add(new LegendItem(label, description, null, 309 null, shape, paint, outlineStroke, outlinePaint)); 310 311 } 312 } 313 else { 314 // in this case, every color has a range of values 315 Set unique = new HashSet(); 316 for (Iterator i = this.paintIndex.entrySet().iterator(); 317 i.hasNext();) { 318 Map.Entry entry = (Map.Entry) i.next(); 319 if (unique.add(entry.getValue())) { 320 String label = getMinPaintValue( 321 (Integer) entry.getValue()).toString() 322 + " - " + getMaxPaintValue( 323 (Integer) entry.getValue()).toString(); 324 String description = label; 325 Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); 326 Paint paint = getSeriesPaint( 327 ((Integer) entry.getValue()).intValue() 328 ); 329 Paint outlinePaint = Color.black; 330 Stroke outlineStroke = DEFAULT_STROKE; 331 332 result.add(new LegendItem(label, description, 333 null, null, shape, paint, outlineStroke, 334 outlinePaint)); 335 } 336 } // end foreach map entry 337 } // end else 338 } 339 return result; 340 } 341 342 /** 343 * Returns the minimum chip value assigned to a color 344 * in the paintIndex 345 * 346 * @param index the index. 347 * 348 * @return The value. 349 */ 350 private Number getMinPaintValue(Integer index) { 351 double minValue = Double.POSITIVE_INFINITY; 352 for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { 353 Map.Entry entry = (Map.Entry) i.next(); 354 if (((Integer) entry.getValue()).equals(index)) { 355 if (((Number) entry.getKey()).doubleValue() < minValue) { 356 minValue = ((Number) entry.getKey()).doubleValue(); 357 } 358 } 359 } 360 return new Double(minValue); 361 } 362 363 /** 364 * Returns the maximum chip value assigned to a color 365 * in the paintIndex 366 * 367 * @param index the index. 368 * 369 * @return The value 370 */ 371 private Number getMaxPaintValue(Integer index) { 372 double maxValue = Double.NEGATIVE_INFINITY; 373 for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { 374 Map.Entry entry = (Map.Entry) i.next(); 375 if (((Integer) entry.getValue()).equals(index)) { 376 if (((Number) entry.getKey()).doubleValue() > maxValue) { 377 maxValue = ((Number) entry.getKey()).doubleValue(); 378 } 379 } 380 } 381 return new Double(maxValue); 382 } 383 384 385 } // end class wafermaprenderer