    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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, 2004, 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     *
    044     */
    045
    046    package org.jfree.chart.plot;
    047
    048    import java.awt.BasicStroke;
    049    import java.awt.Color;
    050    import java.awt.Graphics2D;
    051    import java.awt.Paint;
    052    import java.awt.Shape;
    053    import java.awt.Stroke;
    054    import java.awt.geom.Arc2D;
    055    import java.awt.geom.Ellipse2D;
    056    import java.awt.geom.Point2D;
    057    import java.awt.geom.Rectangle2D;
    058    import java.io.Serializable;
    059    import java.util.ResourceBundle;
    060
    061    import org.jfree.chart.LegendItemCollection;
    062    import org.jfree.chart.event.PlotChangeEvent;
    063    import org.jfree.chart.event.RendererChangeEvent;
    064    import org.jfree.chart.event.RendererChangeListener;
    065    import org.jfree.chart.renderer.WaferMapRenderer;
    066    import org.jfree.data.general.DatasetChangeEvent;
    067    import org.jfree.data.general.WaferMapDataset;
    068    import org.jfree.ui.RectangleInsets;
    069
    070    /**
    071     * A wafer map plot.
    072     */
    073    public class WaferMapPlot extends Plot implements RendererChangeListener,
    074                                                      Cloneable,
    075                                                      Serializable {
    076
    077        /** For serialization. */
    078        private static final long serialVersionUID = 4668320403707308155L;
    079
    080        /** The default grid line stroke. */
    081        public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
    082            BasicStroke.CAP_BUTT,
    083            BasicStroke.JOIN_BEVEL,
    084            0.0f,
    085            new float[] {2.0f, 2.0f},
    086            0.0f);
    087
    088        /** The default grid line paint. */
    089        public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
    090
    091        /** The default crosshair visibility. */
    092        public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
    093
    094        /** The default crosshair stroke. */
    095        public static final Stroke DEFAULT_CROSSHAIR_STROKE
    096            = DEFAULT_GRIDLINE_STROKE;
    097
    098        /** The default crosshair paint. */
    099        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    }