Source for org.jfree.chart.plot.WaferMapPlot

   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
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * -----------------
  28:  * WaferMapPlot.java
  29:  * -----------------
  30:  *
  31:  * (C) Copyright 2003, 2004, by Robert Redburn and Contributors.
  32:  *
  33:  * Original Author:  Robert Redburn;
  34:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  35:  *
  36:  * Changes
  37:  * -------
  38:  * 25-Nov-2003 : Version 1 contributed by Robert Redburn (DG);
  39:  * 05-May-2005 : Updated draw() method parameters (DG);
  40:  * 10-Jun-2005 : Changed private --> protected for drawChipGrid(), 
  41:  *               drawWaferEdge() and getWafterEdge() (DG);
  42:  * 16-Jun-2005 : Added default constructor and setDataset() method (DG);
  43:  *
  44:  */
  45:  
  46: package org.jfree.chart.plot;
  47: 
  48: import java.awt.BasicStroke;
  49: import java.awt.Color;
  50: import java.awt.Graphics2D;
  51: import java.awt.Paint;
  52: import java.awt.Shape;
  53: import java.awt.Stroke;
  54: import java.awt.geom.Arc2D;
  55: import java.awt.geom.Ellipse2D;
  56: import java.awt.geom.Point2D;
  57: import java.awt.geom.Rectangle2D;
  58: import java.io.Serializable;
  59: import java.util.ResourceBundle;
  60: 
  61: import org.jfree.chart.LegendItemCollection;
  62: import org.jfree.chart.event.PlotChangeEvent;
  63: import org.jfree.chart.event.RendererChangeEvent;
  64: import org.jfree.chart.event.RendererChangeListener;
  65: import org.jfree.chart.renderer.WaferMapRenderer;
  66: import org.jfree.data.general.DatasetChangeEvent;
  67: import org.jfree.data.general.WaferMapDataset;
  68: import org.jfree.ui.RectangleInsets;
  69: 
  70: /**
  71:  * A wafer map plot.
  72:  */
  73: public class WaferMapPlot extends Plot implements RendererChangeListener,
  74:                                                   Cloneable,
  75:                                                   Serializable {
  76: 
  77:     /** For serialization. */
  78:     private static final long serialVersionUID = 4668320403707308155L;
  79:     
  80:     /** The default grid line stroke. */
  81:     public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
  82:         BasicStroke.CAP_BUTT,
  83:         BasicStroke.JOIN_BEVEL,
  84:         0.0f,
  85:         new float[] {2.0f, 2.0f},
  86:         0.0f);
  87: 
  88:     /** The default grid line paint. */
  89:     public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
  90: 
  91:     /** The default crosshair visibility. */
  92:     public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
  93: 
  94:     /** The default crosshair stroke. */
  95:     public static final Stroke DEFAULT_CROSSHAIR_STROKE 
  96:         = DEFAULT_GRIDLINE_STROKE;
  97: 
  98:     /** The default crosshair paint. */
  99:     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: 
 212:         if (this.renderer != null) {
 213:             this.renderer.removeChangeListener(this);
 214:         }
 215: 
 216:         this.renderer = renderer;
 217:         if (renderer != null) {
 218:             renderer.setPlot(this);
 219:         }
 220: 
 221:         notifyListeners(new PlotChangeEvent(this));
 222: 
 223:     }
 224:     
 225:     /**
 226:      * Draws the wafermap view.
 227:      * 
 228:      * @param g2  the graphics device.
 229:      * @param area  the plot area.
 230:      * @param anchor  the anchor point (<code>null</code> permitted).
 231:      * @param state  the plot state.
 232:      * @param info  the plot rendering info.
 233:      */
 234:     public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
 235:                      PlotState state, 
 236:                      PlotRenderingInfo info) {
 237: 
 238:         // if the plot area is too small, just return...
 239:         boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
 240:         boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
 241:         if (b1 || b2) {
 242:             return;
 243:         }
 244: 
 245:         // record the plot area...
 246:         if (info != null) {
 247:             info.setPlotArea(area);
 248:         }
 249: 
 250:         // adjust the drawing area for the plot insets (if any)...
 251:         RectangleInsets insets = getInsets();
 252:         insets.trim(area);
 253: 
 254:         drawChipGrid(g2, area);       
 255:         drawWaferEdge(g2, area);
 256:         
 257:     }
 258: 
 259:     /**
 260:      * Calculates and draws the chip locations on the wafer.
 261:      * 
 262:      * @param g2  the graphics device.
 263:      * @param plotArea  the plot area.
 264:      */
 265:     protected void drawChipGrid(Graphics2D g2, Rectangle2D plotArea) {
 266:         
 267:         Shape savedClip = g2.getClip();
 268:         g2.setClip(getWaferEdge(plotArea));
 269:         Rectangle2D chip = new Rectangle2D.Double();
 270:         int xchips = 35;
 271:         int ychips = 20;
 272:         double space = 1d;
 273:         if (this.dataset != null) {
 274:             xchips = this.dataset.getMaxChipX() + 2;
 275:             ychips = this.dataset.getMaxChipY() + 2;
 276:             space = this.dataset.getChipSpace();
 277:         }
 278:         double startX = plotArea.getX();
 279:         double startY = plotArea.getY();
 280:         double chipWidth = 1d;
 281:         double chipHeight = 1d;
 282:         if (plotArea.getWidth() != plotArea.getHeight()) {
 283:             double major = 0d;
 284:             double minor = 0d;
 285:             if (plotArea.getWidth() > plotArea.getHeight()) {
 286:                 major = plotArea.getWidth();
 287:                 minor = plotArea.getHeight();
 288:             } 
 289:             else {
 290:                 major = plotArea.getHeight();
 291:                 minor = plotArea.getWidth();
 292:             } 
 293:             //set upperLeft point
 294:             if (plotArea.getWidth() == minor) { // x is minor
 295:                 startY += (major - minor) / 2;
 296:                 chipWidth = (plotArea.getWidth() - (space * xchips - 1)) 
 297:                     / xchips;
 298:                 chipHeight = (plotArea.getWidth() - (space * ychips - 1)) 
 299:                     / ychips;
 300:             }
 301:             else { // y is minor
 302:                 startX += (major - minor) / 2;
 303:                 chipWidth = (plotArea.getHeight() - (space * xchips - 1)) 
 304:                     / xchips;
 305:                 chipHeight = (plotArea.getHeight() - (space * ychips - 1)) 
 306:                     / ychips;
 307:             }
 308:         }
 309:         
 310:         for (int x = 1; x <= xchips; x++) {
 311:             double upperLeftX = (startX - chipWidth) + (chipWidth * x) 
 312:                 + (space * (x - 1));
 313:             for (int y = 1; y <= ychips; y++) {
 314:                 double upperLeftY = (startY - chipHeight) + (chipHeight * y) 
 315:                     + (space * (y - 1));
 316:                 chip.setFrame(upperLeftX, upperLeftY, chipWidth, chipHeight);
 317:                 g2.setColor(Color.white);
 318:                 if (this.dataset.getChipValue(x - 1, ychips - y - 1) != null) {
 319:                     g2.setPaint(
 320:                         this.renderer.getChipColor(
 321:                             this.dataset.getChipValue(x - 1, ychips - y - 1)
 322:                         )
 323:                     );
 324:                 } 
 325:                 g2.fill(chip);
 326:                 g2.setColor(Color.lightGray);
 327:                 g2.draw(chip);
 328:             }
 329:         }
 330:         g2.setClip(savedClip);
 331:     }
 332: 
 333:     /**
 334:      * Calculates the location of the waferedge.
 335:      * 
 336:      * @param plotArea  the plot area.
 337:      * 
 338:      * @return The wafer edge.
 339:      */
 340:     protected Ellipse2D getWaferEdge(Rectangle2D plotArea) {
 341:         Ellipse2D edge = new Ellipse2D.Double();
 342:         double diameter = plotArea.getWidth();
 343:         double upperLeftX = plotArea.getX();
 344:         double upperLeftY = plotArea.getY();
 345:         //get major dimension
 346:         if (plotArea.getWidth() != plotArea.getHeight()) {
 347:             double major = 0d;
 348:             double minor = 0d;
 349:             if (plotArea.getWidth() > plotArea.getHeight()) {
 350:                 major = plotArea.getWidth();
 351:                 minor = plotArea.getHeight();
 352:             } 
 353:             else {
 354:                 major = plotArea.getHeight();
 355:                 minor = plotArea.getWidth();
 356:             } 
 357:             //ellipse diameter is the minor dimension
 358:             diameter = minor;
 359:             //set upperLeft point
 360:             if (plotArea.getWidth() == minor) { // x is minor
 361:                 upperLeftY = plotArea.getY() + (major - minor) / 2;
 362:             }
 363:             else { // y is minor
 364:                 upperLeftX = plotArea.getX() + (major - minor) / 2;
 365:             }
 366:         }
 367:         edge.setFrame(upperLeftX, upperLeftY, diameter, diameter); 
 368:         return edge;        
 369:     }
 370: 
 371:     /**
 372:      * Draws the waferedge, including the notch.
 373:      * 
 374:      * @param g2  the graphics device.
 375:      * @param plotArea  the plot area.
 376:      */
 377:     protected void drawWaferEdge(Graphics2D g2, Rectangle2D plotArea) {
 378:         // draw the wafer
 379:         Ellipse2D waferEdge = getWaferEdge(plotArea);
 380:         g2.setColor(Color.black);
 381:         g2.draw(waferEdge);
 382:         // calculate and draw the notch
 383:         // horizontal orientation is considered notch right
 384:         // vertical orientation is considered notch down
 385:         Arc2D notch = null;
 386:         Rectangle2D waferFrame = waferEdge.getFrame();
 387:         double notchDiameter = waferFrame.getWidth() * 0.04;
 388:         if (this.orientation == PlotOrientation.HORIZONTAL) {
 389:             Rectangle2D notchFrame = 
 390:                 new Rectangle2D.Double(
 391:                     waferFrame.getX() + waferFrame.getWidth() 
 392:                     - (notchDiameter / 2), waferFrame.getY()
 393:                     + (waferFrame.getHeight() / 2) - (notchDiameter / 2),
 394:                     notchDiameter, notchDiameter
 395:                 );
 396:             notch = new Arc2D.Double(notchFrame, 90d, 180d, Arc2D.OPEN);
 397:         }
 398:         else {
 399:             Rectangle2D notchFrame = 
 400:                 new Rectangle2D.Double(
 401:                     waferFrame.getX() + (waferFrame.getWidth() / 2) 
 402:                     - (notchDiameter / 2), waferFrame.getY() 
 403:                     + waferFrame.getHeight() - (notchDiameter / 2),
 404:                     notchDiameter, notchDiameter
 405:                 );
 406:             notch = new Arc2D.Double(notchFrame, 0d, 180d, Arc2D.OPEN);        
 407:         }
 408:         g2.setColor(Color.white);
 409:         g2.fill(notch);
 410:         g2.setColor(Color.black);
 411:         g2.draw(notch);
 412:         
 413:     }
 414: 
 415:     /**
 416:      * Return the legend items from the renderer.
 417:      * 
 418:      * @return The legend items.
 419:      */
 420:     public LegendItemCollection getLegendItems() {
 421:         return this.renderer.getLegendCollection();
 422:     }
 423: 
 424:     /**
 425:      * Notifies all registered listeners of a renderer change.
 426:      *
 427:      * @param event  the event.
 428:      */
 429:     public void rendererChanged(RendererChangeEvent event) {
 430:         notifyListeners(new PlotChangeEvent(this));
 431:     }
 432: 
 433: }