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: } else {
123: this .paintLimit = paintLimit.intValue();
124: }
125:
126: this .paintIndexMethod = VALUE_INDEX;
127: if (paintIndexMethod != null) {
128: if (isMethodValid(paintIndexMethod.intValue())) {
129: this .paintIndexMethod = paintIndexMethod.intValue();
130: }
131: }
132: }
133:
134: /**
135: * Verifies that the passed paint index method is valid.
136: *
137: * @param method the method.
138: *
139: * @return <code>true</code> or </code>false</code>.
140: */
141: private boolean isMethodValid(int method) {
142: switch (method) {
143: case POSITION_INDEX:
144: return true;
145: case VALUE_INDEX:
146: return true;
147: default:
148: return false;
149: }
150: }
151:
152: /**
153: * Returns the drawing supplier from the plot.
154: *
155: * @return The drawing supplier.
156: */
157: public DrawingSupplier getDrawingSupplier() {
158: DrawingSupplier result = null;
159: WaferMapPlot p = getPlot();
160: if (p != null) {
161: result = p.getDrawingSupplier();
162: }
163: return result;
164: }
165:
166: /**
167: * Returns the plot.
168: *
169: * @return The plot.
170: */
171: public WaferMapPlot getPlot() {
172: return this .plot;
173: }
174:
175: /**
176: * Sets the plot and build the paint index.
177: *
178: * @param plot the plot.
179: */
180: public void setPlot(WaferMapPlot plot) {
181: this .plot = plot;
182: makePaintIndex();
183: }
184:
185: /**
186: * Returns the paint for a given chip value.
187: *
188: * @param value the value.
189: *
190: * @return The paint.
191: */
192: public Paint getChipColor(Number value) {
193: return getSeriesPaint(getPaintIndex(value));
194: }
195:
196: /**
197: * Returns the paint index for a given chip value.
198: *
199: * @param value the value.
200: *
201: * @return The paint index.
202: */
203: private int getPaintIndex(Number value) {
204: return ((Integer) this .paintIndex.get(value)).intValue();
205: }
206:
207: /**
208: * Builds a map of chip values to paint colors.
209: * paintlimit is the maximum allowed number of colors.
210: */
211: private void makePaintIndex() {
212: if (this .plot == null) {
213: return;
214: }
215: WaferMapDataset data = this .plot.getDataset();
216: Number dataMin = data.getMinValue();
217: Number dataMax = data.getMaxValue();
218: Set uniqueValues = data.getUniqueValues();
219: if (uniqueValues.size() <= this .paintLimit) {
220: int count = 0; // assign a color for each unique value
221: for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
222: this .paintIndex.put(i.next(), new Integer(count++));
223: }
224: } else {
225: // more values than paints so map
226: // multiple values to the same color
227: switch (this .paintIndexMethod) {
228: case POSITION_INDEX:
229: makePositionIndex(uniqueValues);
230: break;
231: case VALUE_INDEX:
232: makeValueIndex(dataMax, dataMin, uniqueValues);
233: break;
234: default:
235: break;
236: }
237: }
238: }
239:
240: /**
241: * Builds the paintindex by assigning colors based on the number
242: * of unique values: totalvalues/totalcolors.
243: *
244: * @param uniqueValues the set of unique values.
245: */
246: private void makePositionIndex(Set uniqueValues) {
247: int valuesPerColor = (int) Math.ceil((double) uniqueValues
248: .size()
249: / this .paintLimit);
250: int count = 0; // assign a color for each unique value
251: int paint = 0;
252: for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
253: this .paintIndex.put(i.next(), new Integer(paint));
254: if (++count % valuesPerColor == 0) {
255: paint++;
256: }
257: if (paint > this .paintLimit) {
258: paint = this .paintLimit;
259: }
260: }
261: }
262:
263: /**
264: * Builds the paintindex by assigning colors evenly across the range
265: * of values: maxValue-minValue/totalcolors
266: *
267: * @param max the maximum value.
268: * @param min the minumum value.
269: * @param uniqueValues the unique values.
270: */
271: private void makeValueIndex(Number max, Number min, Set uniqueValues) {
272: double valueRange = max.doubleValue() - min.doubleValue();
273: double valueStep = valueRange / this .paintLimit;
274: int paint = 0;
275: double cutPoint = min.doubleValue() + valueStep;
276: for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
277: Number value = (Number) i.next();
278: while (value.doubleValue() > cutPoint) {
279: cutPoint += valueStep;
280: paint++;
281: if (paint > this .paintLimit) {
282: paint = this .paintLimit;
283: }
284: }
285: this .paintIndex.put(value, new Integer(paint));
286: }
287: }
288:
289: /**
290: * Builds the list of legend entries. called by getLegendItems in
291: * WaferMapPlot to populate the plot legend.
292: *
293: * @return The legend items.
294: */
295: public LegendItemCollection getLegendCollection() {
296: LegendItemCollection result = new LegendItemCollection();
297: if (this .paintIndex != null && this .paintIndex.size() > 0) {
298: if (this .paintIndex.size() <= this .paintLimit) {
299: for (Iterator i = this .paintIndex.entrySet().iterator(); i
300: .hasNext();) {
301: // in this case, every color has a unique value
302: Map.Entry entry = (Map.Entry) i.next();
303: String label = entry.getKey().toString();
304: String description = label;
305: Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
306: Paint paint = getSeriesPaint(((Integer) entry
307: .getValue()).intValue());
308: Paint outlinePaint = Color.black;
309: Stroke outlineStroke = DEFAULT_STROKE;
310:
311: result.add(new LegendItem(label, description, null,
312: null, shape, paint, outlineStroke,
313: outlinePaint));
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(); i
320: .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: + " - "
326: + getMaxPaintValue(
327: (Integer) entry.getValue())
328: .toString();
329: String description = label;
330: Shape shape = new Rectangle2D.Double(1d, 1d,
331: 1d, 1d);
332: Paint paint = getSeriesPaint(((Integer) entry
333: .getValue()).intValue());
334: Paint outlinePaint = Color.black;
335: Stroke outlineStroke = DEFAULT_STROKE;
336:
337: result.add(new LegendItem(label, description,
338: null, null, shape, paint,
339: outlineStroke, outlinePaint));
340: }
341: } // end foreach map entry
342: } // end else
343: }
344: return result;
345: }
346:
347: /**
348: * Returns the minimum chip value assigned to a color
349: * in the paintIndex
350: *
351: * @param index the index.
352: *
353: * @return The value.
354: */
355: private Number getMinPaintValue(Integer index) {
356: double minValue = Double.POSITIVE_INFINITY;
357: for (Iterator i = this .paintIndex.entrySet().iterator(); i
358: .hasNext();) {
359: Map.Entry entry = (Map.Entry) i.next();
360: if (((Integer) entry.getValue()).equals(index)) {
361: if (((Number) entry.getKey()).doubleValue() < minValue) {
362: minValue = ((Number) entry.getKey()).doubleValue();
363: }
364: }
365: }
366: return new Double(minValue);
367: }
368:
369: /**
370: * Returns the maximum chip value assigned to a color
371: * in the paintIndex
372: *
373: * @param index the index.
374: *
375: * @return The value
376: */
377: private Number getMaxPaintValue(Integer index) {
378: double maxValue = Double.NEGATIVE_INFINITY;
379: for (Iterator i = this .paintIndex.entrySet().iterator(); i
380: .hasNext();) {
381: Map.Entry entry = (Map.Entry) i.next();
382: if (((Integer) entry.getValue()).equals(index)) {
383: if (((Number) entry.getKey()).doubleValue() > maxValue) {
384: maxValue = ((Number) entry.getKey()).doubleValue();
385: }
386: }
387: }
388: return new Double(maxValue);
389: }
390:
391: } // end class wafermaprenderer
|