    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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 * [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-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
    048package org.jfree.chart.plot;
    049
    050import java.awt.BasicStroke;
    051import java.awt.Color;
    052import java.awt.Graphics2D;
    053import java.awt.Paint;
    054import java.awt.Shape;
    055import java.awt.Stroke;
    056import java.awt.geom.Arc2D;
    057import java.awt.geom.Ellipse2D;
    058import java.awt.geom.Point2D;
    059import java.awt.geom.Rectangle2D;
    060import java.io.Serializable;
    061import java.util.ResourceBundle;
    062
    063import org.jfree.chart.LegendItemCollection;
    064import org.jfree.chart.event.PlotChangeEvent;
    065import org.jfree.chart.event.RendererChangeEvent;
    066import org.jfree.chart.event.RendererChangeListener;
    067import org.jfree.chart.renderer.WaferMapRenderer;
    068import org.jfree.chart.util.ResourceBundleWrapper;
    069import org.jfree.data.general.DatasetChangeEvent;
    070import org.jfree.data.general.WaferMapDataset;
    071import org.jfree.ui.RectangleInsets;
    072
    073/**
    074 * A wafer map plot.
    075 */
    076public 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}