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