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