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