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
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}