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 * 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 = getSeriesPaint(
304 ((Integer) entry.getValue()).intValue()
305 );
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