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