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