001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * WaferMapPlot.java 029 * ----------------- 030 * 031 * (C) Copyright 2003, 2004, by Robert Redburn and Contributors. 032 * 033 * Original Author: Robert Redburn; 034 * Contributor(s): David Gilbert (for Object Refinery Limited); 035 * 036 * Changes 037 * ------- 038 * 25-Nov-2003 : Version 1 contributed by Robert Redburn (DG); 039 * 05-May-2005 : Updated draw() method parameters (DG); 040 * 10-Jun-2005 : Changed private --> protected for drawChipGrid(), 041 * drawWaferEdge() and getWafterEdge() (DG); 042 * 16-Jun-2005 : Added default constructor and setDataset() method (DG); 043 * 044 */ 045 046 package org.jfree.chart.plot; 047 048 import java.awt.BasicStroke; 049 import java.awt.Color; 050 import java.awt.Graphics2D; 051 import java.awt.Paint; 052 import java.awt.Shape; 053 import java.awt.Stroke; 054 import java.awt.geom.Arc2D; 055 import java.awt.geom.Ellipse2D; 056 import java.awt.geom.Point2D; 057 import java.awt.geom.Rectangle2D; 058 import java.io.Serializable; 059 import java.util.ResourceBundle; 060 061 import org.jfree.chart.LegendItemCollection; 062 import org.jfree.chart.event.PlotChangeEvent; 063 import org.jfree.chart.event.RendererChangeEvent; 064 import org.jfree.chart.event.RendererChangeListener; 065 import org.jfree.chart.renderer.WaferMapRenderer; 066 import org.jfree.data.general.DatasetChangeEvent; 067 import org.jfree.data.general.WaferMapDataset; 068 import org.jfree.ui.RectangleInsets; 069 070 /** 071 * A wafer map plot. 072 */ 073 public class WaferMapPlot extends Plot implements RendererChangeListener, 074 Cloneable, 075 Serializable { 076 077 /** For serialization. */ 078 private static final long serialVersionUID = 4668320403707308155L; 079 080 /** The default grid line stroke. */ 081 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, 082 BasicStroke.CAP_BUTT, 083 BasicStroke.JOIN_BEVEL, 084 0.0f, 085 new float[] {2.0f, 2.0f}, 086 0.0f); 087 088 /** The default grid line paint. */ 089 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray; 090 091 /** The default crosshair visibility. */ 092 public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false; 093 094 /** The default crosshair stroke. */ 095 public static final Stroke DEFAULT_CROSSHAIR_STROKE 096 = DEFAULT_GRIDLINE_STROKE; 097 098 /** The default crosshair paint. */ 099 public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue; 100 101 /** The resourceBundle for the localization. */ 102 protected static ResourceBundle localizationResources = 103 ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); 104 105 /** The plot orientation. 106 * vertical = notch down 107 * horizontal = notch right 108 */ 109 private PlotOrientation orientation; 110 111 /** The dataset. */ 112 private WaferMapDataset dataset; 113 114 /** 115 * Object responsible for drawing the visual representation of each point 116 * on the plot. 117 */ 118 private WaferMapRenderer renderer; 119 120 /** 121 * Creates a new plot with no dataset. 122 */ 123 public WaferMapPlot() { 124 this(null); 125 } 126 127 /** 128 * Creates a new plot. 129 * 130 * @param dataset the dataset (<code>null</code> permitted). 131 */ 132 public WaferMapPlot(WaferMapDataset dataset) { 133 this(dataset, null); 134 } 135 136 /** 137 * Creates a new plot. 138 * 139 * @param dataset the dataset (<code>null</code> permitted). 140 * @param renderer the renderer (<code>null</code> permitted). 141 */ 142 public WaferMapPlot(WaferMapDataset dataset, WaferMapRenderer renderer) { 143 144 super(); 145 146 this.orientation = PlotOrientation.VERTICAL; 147 148 this.dataset = dataset; 149 if (dataset != null) { 150 dataset.addChangeListener(this); 151 } 152 153 this.renderer = renderer; 154 if (renderer != null) { 155 renderer.setPlot(this); 156 renderer.addChangeListener(this); 157 } 158 159 } 160 161 /** 162 * Returns the plot type as a string. 163 * 164 * @return A short string describing the type of plot. 165 */ 166 public String getPlotType() { 167 return ("WMAP_Plot"); 168 } 169 170 /** 171 * Returns the dataset 172 * 173 * @return The dataset (possibly <code>null</code>). 174 */ 175 public WaferMapDataset getDataset() { 176 return this.dataset; 177 } 178 179 /** 180 * Sets the dataset used by the plot and sends a {@link PlotChangeEvent} 181 * to all registered listeners. 182 * 183 * @param dataset the dataset (<code>null</code> permitted). 184 */ 185 public void setDataset(WaferMapDataset dataset) { 186 // if there is an existing dataset, remove the plot from the list of 187 // change listeners... 188 if (this.dataset != null) { 189 this.dataset.removeChangeListener(this); 190 } 191 192 // set the new dataset, and register the chart as a change listener... 193 this.dataset = dataset; 194 if (dataset != null) { 195 setDatasetGroup(dataset.getGroup()); 196 dataset.addChangeListener(this); 197 } 198 199 // send a dataset change event to self to trigger plot change event 200 datasetChanged(new DatasetChangeEvent(this, dataset)); 201 } 202 203 /** 204 * Sets the item renderer, and notifies all listeners of a change to the 205 * plot. If the renderer is set to <code>null</code>, no chart will be 206 * drawn. 207 * 208 * @param renderer the new renderer (<code>null</code> permitted). 209 */ 210 public void setRenderer(WaferMapRenderer renderer) { 211 if (this.renderer != null) { 212 this.renderer.removeChangeListener(this); 213 } 214 this.renderer = renderer; 215 if (renderer != null) { 216 renderer.setPlot(this); 217 } 218 fireChangeEvent(); 219 } 220 221 /** 222 * Draws the wafermap view. 223 * 224 * @param g2 the graphics device. 225 * @param area the plot area. 226 * @param anchor the anchor point (<code>null</code> permitted). 227 * @param state the plot state. 228 * @param info the plot rendering info. 229 */ 230 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 231 PlotState state, 232 PlotRenderingInfo info) { 233 234 // if the plot area is too small, just return... 235 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); 236 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); 237 if (b1 || b2) { 238 return; 239 } 240 241 // record the plot area... 242 if (info != null) { 243 info.setPlotArea(area); 244 } 245 246 // adjust the drawing area for the plot insets (if any)... 247 RectangleInsets insets = getInsets(); 248 insets.trim(area); 249 250 drawChipGrid(g2, area); 251 drawWaferEdge(g2, area); 252 253 } 254 255 /** 256 * Calculates and draws the chip locations on the wafer. 257 * 258 * @param g2 the graphics device. 259 * @param plotArea the plot area. 260 */ 261 protected void drawChipGrid(Graphics2D g2, Rectangle2D plotArea) { 262 263 Shape savedClip = g2.getClip(); 264 g2.setClip(getWaferEdge(plotArea)); 265 Rectangle2D chip = new Rectangle2D.Double(); 266 int xchips = 35; 267 int ychips = 20; 268 double space = 1d; 269 if (this.dataset != null) { 270 xchips = this.dataset.getMaxChipX() + 2; 271 ychips = this.dataset.getMaxChipY() + 2; 272 space = this.dataset.getChipSpace(); 273 } 274 double startX = plotArea.getX(); 275 double startY = plotArea.getY(); 276 double chipWidth = 1d; 277 double chipHeight = 1d; 278 if (plotArea.getWidth() != plotArea.getHeight()) { 279 double major = 0d; 280 double minor = 0d; 281 if (plotArea.getWidth() > plotArea.getHeight()) { 282 major = plotArea.getWidth(); 283 minor = plotArea.getHeight(); 284 } 285 else { 286 major = plotArea.getHeight(); 287 minor = plotArea.getWidth(); 288 } 289 //set upperLeft point 290 if (plotArea.getWidth() == minor) { // x is minor 291 startY += (major - minor) / 2; 292 chipWidth = (plotArea.getWidth() - (space * xchips - 1)) 293 / xchips; 294 chipHeight = (plotArea.getWidth() - (space * ychips - 1)) 295 / ychips; 296 } 297 else { // y is minor 298 startX += (major - minor) / 2; 299 chipWidth = (plotArea.getHeight() - (space * xchips - 1)) 300 / xchips; 301 chipHeight = (plotArea.getHeight() - (space * ychips - 1)) 302 / ychips; 303 } 304 } 305 306 for (int x = 1; x <= xchips; x++) { 307 double upperLeftX = (startX - chipWidth) + (chipWidth * x) 308 + (space * (x - 1)); 309 for (int y = 1; y <= ychips; y++) { 310 double upperLeftY = (startY - chipHeight) + (chipHeight * y) 311 + (space * (y - 1)); 312 chip.setFrame(upperLeftX, upperLeftY, chipWidth, chipHeight); 313 g2.setColor(Color.white); 314 if (this.dataset.getChipValue(x - 1, ychips - y - 1) != null) { 315 g2.setPaint( 316 this.renderer.getChipColor( 317 this.dataset.getChipValue(x - 1, ychips - y - 1) 318 ) 319 ); 320 } 321 g2.fill(chip); 322 g2.setColor(Color.lightGray); 323 g2.draw(chip); 324 } 325 } 326 g2.setClip(savedClip); 327 } 328 329 /** 330 * Calculates the location of the waferedge. 331 * 332 * @param plotArea the plot area. 333 * 334 * @return The wafer edge. 335 */ 336 protected Ellipse2D getWaferEdge(Rectangle2D plotArea) { 337 Ellipse2D edge = new Ellipse2D.Double(); 338 double diameter = plotArea.getWidth(); 339 double upperLeftX = plotArea.getX(); 340 double upperLeftY = plotArea.getY(); 341 //get major dimension 342 if (plotArea.getWidth() != plotArea.getHeight()) { 343 double major = 0d; 344 double minor = 0d; 345 if (plotArea.getWidth() > plotArea.getHeight()) { 346 major = plotArea.getWidth(); 347 minor = plotArea.getHeight(); 348 } 349 else { 350 major = plotArea.getHeight(); 351 minor = plotArea.getWidth(); 352 } 353 //ellipse diameter is the minor dimension 354 diameter = minor; 355 //set upperLeft point 356 if (plotArea.getWidth() == minor) { // x is minor 357 upperLeftY = plotArea.getY() + (major - minor) / 2; 358 } 359 else { // y is minor 360 upperLeftX = plotArea.getX() + (major - minor) / 2; 361 } 362 } 363 edge.setFrame(upperLeftX, upperLeftY, diameter, diameter); 364 return edge; 365 } 366 367 /** 368 * Draws the waferedge, including the notch. 369 * 370 * @param g2 the graphics device. 371 * @param plotArea the plot area. 372 */ 373 protected void drawWaferEdge(Graphics2D g2, Rectangle2D plotArea) { 374 // draw the wafer 375 Ellipse2D waferEdge = getWaferEdge(plotArea); 376 g2.setColor(Color.black); 377 g2.draw(waferEdge); 378 // calculate and draw the notch 379 // horizontal orientation is considered notch right 380 // vertical orientation is considered notch down 381 Arc2D notch = null; 382 Rectangle2D waferFrame = waferEdge.getFrame(); 383 double notchDiameter = waferFrame.getWidth() * 0.04; 384 if (this.orientation == PlotOrientation.HORIZONTAL) { 385 Rectangle2D notchFrame = 386 new Rectangle2D.Double( 387 waferFrame.getX() + waferFrame.getWidth() 388 - (notchDiameter / 2), waferFrame.getY() 389 + (waferFrame.getHeight() / 2) - (notchDiameter / 2), 390 notchDiameter, notchDiameter 391 ); 392 notch = new Arc2D.Double(notchFrame, 90d, 180d, Arc2D.OPEN); 393 } 394 else { 395 Rectangle2D notchFrame = 396 new Rectangle2D.Double( 397 waferFrame.getX() + (waferFrame.getWidth() / 2) 398 - (notchDiameter / 2), waferFrame.getY() 399 + waferFrame.getHeight() - (notchDiameter / 2), 400 notchDiameter, notchDiameter 401 ); 402 notch = new Arc2D.Double(notchFrame, 0d, 180d, Arc2D.OPEN); 403 } 404 g2.setColor(Color.white); 405 g2.fill(notch); 406 g2.setColor(Color.black); 407 g2.draw(notch); 408 409 } 410 411 /** 412 * Return the legend items from the renderer. 413 * 414 * @return The legend items. 415 */ 416 public LegendItemCollection getLegendItems() { 417 return this.renderer.getLegendCollection(); 418 } 419 420 /** 421 * Notifies all registered listeners of a renderer change. 422 * 423 * @param event the event. 424 */ 425 public void rendererChanged(RendererChangeEvent event) { 426 fireChangeEvent(); 427 } 428 429 }