001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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    @Override
170    public String getPlotType() {
171        return ("WMAP_Plot");
172    }
173
174    /**
175     * Returns the dataset
176     *
177     * @return The dataset (possibly <code>null</code>).
178     */
179    public WaferMapDataset getDataset() {
180        return this.dataset;
181    }
182
183    /**
184     * Sets the dataset used by the plot and sends a {@link PlotChangeEvent}
185     * to all registered listeners.
186     *
187     * @param dataset  the dataset (<code>null</code> permitted).
188     */
189    public void setDataset(WaferMapDataset dataset) {
190        // if there is an existing dataset, remove the plot from the list of
191        // change listeners...
192        if (this.dataset != null) {
193            this.dataset.removeChangeListener(this);
194        }
195
196        // set the new dataset, and register the chart as a change listener...
197        this.dataset = dataset;
198        if (dataset != null) {
199            setDatasetGroup(dataset.getGroup());
200            dataset.addChangeListener(this);
201        }
202
203        // send a dataset change event to self to trigger plot change event
204        datasetChanged(new DatasetChangeEvent(this, dataset));
205    }
206
207    /**
208     * Sets the item renderer, and notifies all listeners of a change to the
209     * plot.  If the renderer is set to <code>null</code>, no chart will be
210     * drawn.
211     *
212     * @param renderer  the new renderer (<code>null</code> permitted).
213     */
214    public void setRenderer(WaferMapRenderer renderer) {
215        if (this.renderer != null) {
216            this.renderer.removeChangeListener(this);
217        }
218        this.renderer = renderer;
219        if (renderer != null) {
220            renderer.setPlot(this);
221        }
222        fireChangeEvent();
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    @Override
235    public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
236                     PlotState state,
237                     PlotRenderingInfo info) {
238
239        // if the plot area is too small, just return...
240        boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
241        boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
242        if (b1 || b2) {
243            return;
244        }
245
246        // record the plot area...
247        if (info != null) {
248            info.setPlotArea(area);
249        }
250
251        // adjust the drawing area for the plot insets (if any)...
252        RectangleInsets insets = getInsets();
253        insets.trim(area);
254
255        drawChipGrid(g2, area);
256        drawWaferEdge(g2, area);
257
258    }
259
260    /**
261     * Calculates and draws the chip locations on the wafer.
262     *
263     * @param g2  the graphics device.
264     * @param plotArea  the plot area.
265     */
266    protected void drawChipGrid(Graphics2D g2, Rectangle2D plotArea) {
267
268        Shape savedClip = g2.getClip();
269        g2.setClip(getWaferEdge(plotArea));
270        Rectangle2D chip = new Rectangle2D.Double();
271        int xchips = 35;
272        int ychips = 20;
273        double space = 1d;
274        if (this.dataset != null) {
275            xchips = this.dataset.getMaxChipX() + 2;
276            ychips = this.dataset.getMaxChipY() + 2;
277            space = this.dataset.getChipSpace();
278        }
279        double startX = plotArea.getX();
280        double startY = plotArea.getY();
281        double chipWidth = 1d;
282        double chipHeight = 1d;
283        if (plotArea.getWidth() != plotArea.getHeight()) {
284            double major, minor;
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, minor;
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;
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    @Override
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    @Override
430    public void rendererChanged(RendererChangeEvent event) {
431        fireChangeEvent();
432    }
433
434}