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