1. /* ===========================================================
  2. * JFreeChart : a free chart library for the Java(tm) platform
  3. * ===========================================================
  4. *
  5. * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
  6. *
  7. * Project Info: http://www.jfree.org/jfreechart/index.html
  8. *
  9. * This library is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU Lesser General Public License as published by
  11. * the Free Software Foundation; either version 2.1 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17. * License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public License
  20. * along with this library; if not, write to the Free Software Foundation,
  21. * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  22. *
  23. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  24. * in the United States and other countries.]
  25. *
  26. * ---------------------
  27. * WaferMapRenderer.java
  28. * ---------------------
  29. * (C) Copyright 2003-2005, by Robert Redburn and Contributors.
  30. *
  31. * Original Author: Robert Redburn;
  32. * Contributor(s): David Gilbert (for Object Refinery Limited);
  33. *
  34. * $Id: WaferMapRenderer.java,v 1.3 2005/02/09 13:57:23 mungady Exp $
  35. *
  36. * Changes
  37. * -------
  38. * 25-Nov-2003 : Version 1, contributed by Robert Redburn. Changes have been
  39. * made to fit the JFreeChart coding style (DG);
  40. *
  41. */
  42. package org.jfree.chart.renderer;
  43. import java.awt.Color;
  44. import java.awt.Paint;
  45. import java.awt.Shape;
  46. import java.awt.Stroke;
  47. import java.awt.geom.Rectangle2D;
  48. import java.util.HashMap;
  49. import java.util.HashSet;
  50. import java.util.Iterator;
  51. import java.util.Map;
  52. import java.util.Set;
  53. import org.jfree.chart.LegendItem;
  54. import org.jfree.chart.LegendItemCollection;
  55. import org.jfree.chart.plot.DrawingSupplier;
  56. import org.jfree.chart.plot.WaferMapPlot;
  57. import org.jfree.data.general.WaferMapDataset;
  58. /**
  59. * A renderer for wafer map plots. Provides color managment facilities.
  60. *
  61. * @author Robert Redburn.
  62. */
  63. public class WaferMapRenderer extends AbstractRenderer {
  64. /** paint index */
  65. private Map paintIndex;
  66. /** plot */
  67. private WaferMapPlot plot;
  68. /** paint limit */
  69. private int paintLimit;
  70. /** default paint limit */
  71. private static final int DEFAULT_PAINT_LIMIT = 35;
  72. /** default multivalue paint calculation */
  73. public static final int POSITION_INDEX = 0;
  74. /** The default value index. */
  75. public static final int VALUE_INDEX = 1;
  76. /** paint index method */
  77. private int paintIndexMethod;
  78. /**
  79. * Creates a new renderer.
  80. */
  81. public WaferMapRenderer() {
  82. this(null, null);
  83. }
  84. /**
  85. * Creates a new renderer.
  86. *
  87. * @param paintLimit the paint limit.
  88. * @param paintIndexMethod the paint index method.
  89. */
  90. public WaferMapRenderer(int paintLimit, int paintIndexMethod) {
  91. this(new Integer(paintLimit), new Integer(paintIndexMethod));
  92. }
  93. /**
  94. * Creates a new renderer.
  95. *
  96. * @param paintLimit the paint limit.
  97. * @param paintIndexMethod the paint index method.
  98. */
  99. public WaferMapRenderer(Integer paintLimit, Integer paintIndexMethod) {
  100. super();
  101. this.paintIndex = new HashMap();
  102. if (paintLimit == null) {
  103. this.paintLimit = DEFAULT_PAINT_LIMIT;
  104. }
  105. else {
  106. this.paintLimit = paintLimit.intValue();
  107. }
  108. this.paintIndexMethod = VALUE_INDEX;
  109. if (paintIndexMethod != null) {
  110. if (isMethodValid(paintIndexMethod.intValue())) {
  111. this.paintIndexMethod = paintIndexMethod.intValue();
  112. }
  113. }
  114. }
  115. /**
  116. * Verifies that the passed paint index method is valid.
  117. *
  118. * @param method the method.
  119. *
  120. * @return true or false.
  121. */
  122. private boolean isMethodValid(int method) {
  123. switch (method) {
  124. case POSITION_INDEX: return true;
  125. case VALUE_INDEX: return true;
  126. default: return false;
  127. }
  128. }
  129. /**
  130. * Returns the drawing supplier from the plot.
  131. *
  132. * @return the drawing supplier.
  133. */
  134. public DrawingSupplier getDrawingSupplier() {
  135. DrawingSupplier result = null;
  136. WaferMapPlot p = getPlot();
  137. if (p != null) {
  138. result = p.getDrawingSupplier();
  139. }
  140. return result;
  141. }
  142. /**
  143. * Returns the plot.
  144. *
  145. * @return the plot.
  146. */
  147. public WaferMapPlot getPlot() {
  148. return this.plot;
  149. }
  150. /**
  151. * Sets the plot and build the paint index.
  152. *
  153. * @param plot the plot.
  154. */
  155. public void setPlot(WaferMapPlot plot) {
  156. this.plot = plot;
  157. makePaintIndex();
  158. }
  159. /**
  160. *
  161. */
  162. // public void addPropertyChangeListener(PropertyChangeListener ear) {
  163. // }
  164. /**
  165. * Returns the paint for a given chip value.
  166. *
  167. * @param value the value.
  168. *
  169. * @return the paint.
  170. */
  171. public Paint getChipColor(Number value) {
  172. return getSeriesPaint(getPaintIndex(value));
  173. }
  174. /**
  175. * Returns the paint index for a given chip value.
  176. *
  177. * @param value the value.
  178. *
  179. * @return the paint index.
  180. */
  181. private int getPaintIndex(Number value) {
  182. return ((Integer) this.paintIndex.get(value)).intValue();
  183. }
  184. /**
  185. * Builds a map of chip values to paint colors.
  186. * paintlimit is the maximum allowed number of colors.
  187. */
  188. private void makePaintIndex() {
  189. if (this.plot == null) {
  190. return;
  191. }
  192. WaferMapDataset data = this.plot.getDataset();
  193. Number dataMin = data.getMinValue();
  194. Number dataMax = data.getMaxValue();
  195. Set uniqueValues = data.getUniqueValues();
  196. if (uniqueValues.size() <= this.paintLimit) {
  197. int count = 0; // assign a color for each unique value
  198. for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
  199. this.paintIndex.put(i.next(), new Integer(count++));
  200. }
  201. }
  202. else {
  203. // more values than paints so map
  204. // multiple values to the same color
  205. switch (this.paintIndexMethod) {
  206. case POSITION_INDEX:
  207. makePositionIndex(uniqueValues);
  208. break;
  209. case VALUE_INDEX:
  210. makeValueIndex(dataMax, dataMin, uniqueValues);
  211. break;
  212. default:
  213. break;
  214. }
  215. }
  216. }
  217. /**
  218. * Builds the paintindex by assigning colors based on the number
  219. * of unique values: totalvalues/totalcolors.
  220. *
  221. * @param uniqueValues the set of unique values.
  222. */
  223. private void makePositionIndex(Set uniqueValues) {
  224. int valuesPerColor = (int) Math.ceil(
  225. (double) uniqueValues.size() / this.paintLimit
  226. );
  227. int count = 0; // assign a color for each unique value
  228. int paint = 0;
  229. for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
  230. this.paintIndex.put(i.next(), new Integer(paint));
  231. if (++count % valuesPerColor == 0) {
  232. paint++;
  233. }
  234. if (paint > this.paintLimit) {
  235. paint = this.paintLimit;
  236. }
  237. }
  238. }
  239. /**
  240. * Builds the paintindex by assigning colors evenly across the range
  241. * of values: maxValue-minValue/totalcolors
  242. *
  243. * @param max the maximum value.
  244. * @param min the minumum value.
  245. * @param uniqueValues the unique values.
  246. */
  247. private void makeValueIndex(Number max, Number min, Set uniqueValues) {
  248. double valueRange = max.doubleValue() - min.doubleValue();
  249. double valueStep = valueRange / this.paintLimit;
  250. int paint = 0;
  251. double cutPoint = min.doubleValue() + valueStep;
  252. for (Iterator i = uniqueValues.iterator(); i.hasNext();) {
  253. Number value = (Number) i.next();
  254. while (value.doubleValue() > cutPoint) {
  255. cutPoint += valueStep;
  256. paint++;
  257. if (paint > this.paintLimit) {
  258. paint = this.paintLimit;
  259. }
  260. }
  261. this.paintIndex.put(value, new Integer(paint));
  262. }
  263. }
  264. /**
  265. * Builds the list of legend entries. called by getLegendItems in
  266. * WaferMapPlot to populate the plot legend.
  267. *
  268. * @return the legend items.
  269. */
  270. public LegendItemCollection getLegendCollection() {
  271. LegendItemCollection result = new LegendItemCollection();
  272. if (this.paintIndex != null && this.paintIndex.size() > 0) {
  273. if (this.paintIndex.size() <= this.paintLimit) {
  274. for (Iterator i = this.paintIndex.entrySet().iterator();
  275. i.hasNext();) {
  276. // in this case, every color has a unique value
  277. Map.Entry entry = (Map.Entry) i.next();
  278. String label = entry.getKey().toString();
  279. String description = label;
  280. Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
  281. Paint paint = this.getSeriesPaint(
  282. ((Integer) entry.getValue()).intValue()
  283. );
  284. Paint outlinePaint = Color.black;
  285. Stroke outlineStroke = DEFAULT_STROKE;
  286. result.add(
  287. new LegendItem(
  288. label, description, shape, paint,
  289. outlineStroke, outlinePaint
  290. )
  291. );
  292. }
  293. }
  294. else {
  295. // in this case, every color has a range of values
  296. Set unique = new HashSet();
  297. for (Iterator i = this.paintIndex.entrySet().iterator();
  298. i.hasNext();) {
  299. Map.Entry entry = (Map.Entry) i.next();
  300. if (unique.add(entry.getValue())) {
  301. String label = getMinPaintValue(
  302. (Integer) entry.getValue()).toString()
  303. + " - " + getMaxPaintValue(
  304. (Integer) entry.getValue()).toString();
  305. String description = label;
  306. Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d);
  307. Paint paint = getSeriesPaint(
  308. ((Integer) entry.getValue()).intValue()
  309. );
  310. Paint outlinePaint = Color.black;
  311. Stroke outlineStroke = DEFAULT_STROKE;
  312. result.add(new LegendItem(
  313. label, description, shape, paint,
  314. outlineStroke, outlinePaint
  315. ));
  316. }
  317. } // end foreach map entry
  318. } // end else
  319. }
  320. return result;
  321. }
  322. /**
  323. * Returns the minimum chip value assigned to a color
  324. * in the paintIndex
  325. *
  326. * @param index the index.
  327. *
  328. * @return the value.
  329. */
  330. private Number getMinPaintValue(Integer index) {
  331. double minValue = Double.POSITIVE_INFINITY;
  332. for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) {
  333. Map.Entry entry = (Map.Entry) i.next();
  334. if (((Integer) entry.getValue()).equals(index)) {
  335. if (((Number) entry.getKey()).doubleValue() < minValue) {
  336. minValue = ((Number) entry.getKey()).doubleValue();
  337. }
  338. }
  339. }
  340. return new Double(minValue);
  341. }
  342. /**
  343. * Returns the maximum chip value assigned to a color
  344. * in the paintIndex
  345. *
  346. * @param index the index.
  347. *
  348. * @return The value
  349. */
  350. private Number getMaxPaintValue(Integer index) {
  351. double maxValue = Double.NEGATIVE_INFINITY;
  352. for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) {
  353. Map.Entry entry = (Map.Entry) i.next();
  354. if (((Integer) entry.getValue()).equals(index)) {
  355. if (((Number) entry.getKey()).doubleValue() > maxValue) {
  356. maxValue = ((Number) entry.getKey()).doubleValue();
  357. }
  358. }
  359. }
  360. return new Double(maxValue);
  361. }
  362. } // end class wafermaprenderer