    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    }