001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 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 045package org.jfree.chart.renderer; 046 047import java.awt.Color; 048import java.awt.Paint; 049import java.awt.Shape; 050import java.awt.Stroke; 051import java.awt.geom.Rectangle2D; 052import java.util.HashMap; 053import java.util.HashSet; 054import java.util.Iterator; 055import java.util.Map; 056import java.util.Set; 057 058import org.jfree.chart.LegendItem; 059import org.jfree.chart.LegendItemCollection; 060import org.jfree.chart.plot.DrawingSupplier; 061import org.jfree.chart.plot.WaferMapPlot; 062import org.jfree.data.general.WaferMapDataset; 063 064/** 065 * A renderer for wafer map plots. Provides color managment facilities. 066 */ 067public 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 @Override 154 public DrawingSupplier getDrawingSupplier() { 155 DrawingSupplier result = null; 156 WaferMapPlot p = getPlot(); 157 if (p != null) { 158 result = p.getDrawingSupplier(); 159 } 160 return result; 161 } 162 163 /** 164 * Returns the plot. 165 * 166 * @return The plot. 167 */ 168 public WaferMapPlot getPlot() { 169 return this.plot; 170 } 171 172 /** 173 * Sets the plot and build the paint index. 174 * 175 * @param plot the plot. 176 */ 177 public void setPlot(WaferMapPlot plot) { 178 this.plot = plot; 179 makePaintIndex(); 180 } 181 182 /** 183 * Returns the paint for a given chip value. 184 * 185 * @param value the value. 186 * 187 * @return The paint. 188 */ 189 public Paint getChipColor(Number value) { 190 return getSeriesPaint(getPaintIndex(value)); 191 } 192 193 /** 194 * Returns the paint index for a given chip value. 195 * 196 * @param value the value. 197 * 198 * @return The paint index. 199 */ 200 private int getPaintIndex(Number value) { 201 return ((Integer) this.paintIndex.get(value)).intValue(); 202 } 203 204 /** 205 * Builds a map of chip values to paint colors. 206 * paintlimit is the maximum allowed number of colors. 207 */ 208 private void makePaintIndex() { 209 if (this.plot == null) { 210 return; 211 } 212 WaferMapDataset data = this.plot.getDataset(); 213 Number dataMin = data.getMinValue(); 214 Number dataMax = data.getMaxValue(); 215 Set uniqueValues = data.getUniqueValues(); 216 if (uniqueValues.size() <= this.paintLimit) { 217 int count = 0; // assign a color for each unique value 218 for (Iterator i = uniqueValues.iterator(); i.hasNext();) { 219 this.paintIndex.put(i.next(), new Integer(count++)); 220 } 221 } 222 else { 223 // more values than paints so map 224 // multiple values to the same color 225 switch (this.paintIndexMethod) { 226 case POSITION_INDEX: 227 makePositionIndex(uniqueValues); 228 break; 229 case VALUE_INDEX: 230 makeValueIndex(dataMax, dataMin, uniqueValues); 231 break; 232 default: 233 break; 234 } 235 } 236 } 237 238 /** 239 * Builds the paintindex by assigning colors based on the number 240 * of unique values: totalvalues/totalcolors. 241 * 242 * @param uniqueValues the set of unique values. 243 */ 244 private void makePositionIndex(Set uniqueValues) { 245 int valuesPerColor = (int) Math.ceil( 246 (double) uniqueValues.size() / this.paintLimit 247 ); 248 int count = 0; // assign a color for each unique value 249 int paint = 0; 250 for (Iterator i = uniqueValues.iterator(); i.hasNext();) { 251 this.paintIndex.put(i.next(), new Integer(paint)); 252 if (++count % valuesPerColor == 0) { 253 paint++; 254 } 255 if (paint > this.paintLimit) { 256 paint = this.paintLimit; 257 } 258 } 259 } 260 261 /** 262 * Builds the paintindex by assigning colors evenly across the range 263 * of values: maxValue-minValue/totalcolors 264 * 265 * @param max the maximum value. 266 * @param min the minumum value. 267 * @param uniqueValues the unique values. 268 */ 269 private void makeValueIndex(Number max, Number min, Set uniqueValues) { 270 double valueRange = max.doubleValue() - min.doubleValue(); 271 double valueStep = valueRange / this.paintLimit; 272 int paint = 0; 273 double cutPoint = min.doubleValue() + valueStep; 274 for (Iterator i = uniqueValues.iterator(); i.hasNext();) { 275 Number value = (Number) i.next(); 276 while (value.doubleValue() > cutPoint) { 277 cutPoint += valueStep; 278 paint++; 279 if (paint > this.paintLimit) { 280 paint = this.paintLimit; 281 } 282 } 283 this.paintIndex.put(value, new Integer(paint)); 284 } 285 } 286 287 /** 288 * Builds the list of legend entries. called by getLegendItems in 289 * WaferMapPlot to populate the plot legend. 290 * 291 * @return The legend items. 292 */ 293 public LegendItemCollection getLegendCollection() { 294 LegendItemCollection result = new LegendItemCollection(); 295 if (this.paintIndex != null && this.paintIndex.size() > 0) { 296 if (this.paintIndex.size() <= this.paintLimit) { 297 for (Iterator i = this.paintIndex.entrySet().iterator(); 298 i.hasNext();) { 299 // in this case, every color has a unique value 300 Map.Entry entry = (Map.Entry) i.next(); 301 String label = entry.getKey().toString(); 302 String description = label; 303 Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); 304 Paint paint = lookupSeriesPaint( 305 ((Integer) entry.getValue()).intValue()); 306 Paint outlinePaint = Color.black; 307 Stroke outlineStroke = DEFAULT_STROKE; 308 309 result.add(new LegendItem(label, description, null, 310 null, shape, paint, outlineStroke, outlinePaint)); 311 312 } 313 } 314 else { 315 // in this case, every color has a range of values 316 Set unique = new HashSet(); 317 for (Iterator i = this.paintIndex.entrySet().iterator(); 318 i.hasNext();) { 319 Map.Entry entry = (Map.Entry) i.next(); 320 if (unique.add(entry.getValue())) { 321 String label = getMinPaintValue( 322 (Integer) entry.getValue()).toString() 323 + " - " + getMaxPaintValue( 324 (Integer) entry.getValue()).toString(); 325 String description = label; 326 Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); 327 Paint paint = getSeriesPaint( 328 ((Integer) entry.getValue()).intValue() 329 ); 330 Paint outlinePaint = Color.black; 331 Stroke outlineStroke = DEFAULT_STROKE; 332 333 result.add(new LegendItem(label, description, 334 null, null, shape, paint, outlineStroke, 335 outlinePaint)); 336 } 337 } // end foreach map entry 338 } // end else 339 } 340 return result; 341 } 342 343 /** 344 * Returns the minimum chip value assigned to a color 345 * in the paintIndex 346 * 347 * @param index the index. 348 * 349 * @return The value. 350 */ 351 private Number getMinPaintValue(Integer index) { 352 double minValue = Double.POSITIVE_INFINITY; 353 for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { 354 Map.Entry entry = (Map.Entry) i.next(); 355 if (((Integer) entry.getValue()).equals(index)) { 356 if (((Number) entry.getKey()).doubleValue() < minValue) { 357 minValue = ((Number) entry.getKey()).doubleValue(); 358 } 359 } 360 } 361 return new Double(minValue); 362 } 363 364 /** 365 * Returns the maximum chip value assigned to a color 366 * in the paintIndex 367 * 368 * @param index the index. 369 * 370 * @return The value 371 */ 372 private Number getMaxPaintValue(Integer index) { 373 double maxValue = Double.NEGATIVE_INFINITY; 374 for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { 375 Map.Entry entry = (Map.Entry) i.next(); 376 if (((Integer) entry.getValue()).equals(index)) { 377 if (((Number) entry.getKey()).doubleValue() > maxValue) { 378 maxValue = ((Number) entry.getKey()).doubleValue(); 379 } 380 } 381 } 382 return new Double(maxValue); 383 } 384 385 386} // end class wafermaprenderer