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     *
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, Serializable {
075    
076        /** For serialization. */
077        private static final long serialVersionUID = 4668320403707308155L;
078    
079        /** The default grid line stroke. */
080        public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
081            BasicStroke.CAP_BUTT,
082            BasicStroke.JOIN_BEVEL,
083            0.0f,
084            new float[] {2.0f, 2.0f},
085            0.0f);
086    
087        /** The default grid line paint. */
088        public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
089    
090        /** The default crosshair visibility. */
091        public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
092    
093        /** The default crosshair stroke. */
094        public static final Stroke DEFAULT_CROSSHAIR_STROKE
095            = DEFAULT_GRIDLINE_STROKE;
096    
097        /** The default crosshair paint. */
098        public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
099    
100        /** The resourceBundle for the localization. */
101        protected static ResourceBundle localizationResources =
102            ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
103    
104        /** The plot orientation.
105         *  vertical = notch down
106         *  horizontal = notch right
107         */
108        private PlotOrientation orientation;
109    
110        /** The dataset. */
111        private WaferMapDataset dataset;
112    
113        /**
114         * Object responsible for drawing the visual representation of each point
115         * on the plot.
116         */
117        private WaferMapRenderer renderer;
118    
119        /**
120         * Creates a new plot with no dataset.
121         */
122        public WaferMapPlot() {
123            this(null);
124        }
125    
126        /**
127         * Creates a new plot.
128         *
129         * @param dataset  the dataset (<code>null</code> permitted).
130         */
131        public WaferMapPlot(WaferMapDataset dataset) {
132            this(dataset, null);
133        }
134    
135        /**
136         * Creates a new plot.
137         *
138         * @param dataset  the dataset (<code>null</code> permitted).
139         * @param renderer  the renderer (<code>null</code> permitted).
140         */
141        public WaferMapPlot(WaferMapDataset dataset, WaferMapRenderer renderer) {
142    
143            super();
144    
145            this.orientation = PlotOrientation.VERTICAL;
146    
147            this.dataset = dataset;
148            if (dataset != null) {
149                dataset.addChangeListener(this);
150            }
151    
152            this.renderer = renderer;
153            if (renderer != null) {
154                renderer.setPlot(this);
155                renderer.addChangeListener(this);
156            }
157    
158        }
159    
160        /**
161         * Returns the plot type as a string.
162         *
163         * @return A short string describing the type of plot.
164         */
165        public String getPlotType() {
166            return ("WMAP_Plot");
167        }
168    
169        /**
170         * Returns the dataset
171         *
172         * @return The dataset (possibly <code>null</code>).
173         */
174        public WaferMapDataset getDataset() {
175            return this.dataset;
176        }
177    
178        /**
179         * Sets the dataset used by the plot and sends a {@link PlotChangeEvent}
180         * to all registered listeners.
181         *
182         * @param dataset  the dataset (<code>null</code> permitted).
183         */
184        public void setDataset(WaferMapDataset dataset) {
185            // if there is an existing dataset, remove the plot from the list of
186            // change listeners...
187            if (this.dataset != null) {
188                this.dataset.removeChangeListener(this);
189            }
190    
191            // set the new dataset, and register the chart as a change listener...
192            this.dataset = dataset;
193            if (dataset != null) {
194                setDatasetGroup(dataset.getGroup());
195                dataset.addChangeListener(this);
196            }
197    
198            // send a dataset change event to self to trigger plot change event
199            datasetChanged(new DatasetChangeEvent(this, dataset));
200        }
201    
202        /**
203         * Sets the item renderer, and notifies all listeners of a change to the
204         * plot.  If the renderer is set to <code>null</code>, no chart will be
205         * drawn.
206         *
207         * @param renderer  the new renderer (<code>null</code> permitted).
208         */
209        public void setRenderer(WaferMapRenderer renderer) {
210            if (this.renderer != null) {
211                this.renderer.removeChangeListener(this);
212            }
213            this.renderer = renderer;
214            if (renderer != null) {
215                renderer.setPlot(this);
216            }
217            fireChangeEvent();
218        }
219    
220        /**
221         * Draws the wafermap view.
222         *
223         * @param g2  the graphics device.
224         * @param area  the plot area.
225         * @param anchor  the anchor point (<code>null</code> permitted).
226         * @param state  the plot state.
227         * @param info  the plot rendering info.
228         */
229        public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
230                         PlotState state,
231                         PlotRenderingInfo info) {
232    
233            // if the plot area is too small, just return...
234            boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
235            boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
236            if (b1 || b2) {
237                return;
238            }
239    
240            // record the plot area...
241            if (info != null) {
242                info.setPlotArea(area);
243            }
244    
245            // adjust the drawing area for the plot insets (if any)...
246            RectangleInsets insets = getInsets();
247            insets.trim(area);
248    
249            drawChipGrid(g2, area);
250            drawWaferEdge(g2, area);
251    
252        }
253    
254        /**
255         * Calculates and draws the chip locations on the wafer.
256         *
257         * @param g2  the graphics device.
258         * @param plotArea  the plot area.
259         */
260        protected void drawChipGrid(Graphics2D g2, Rectangle2D plotArea) {
261    
262            Shape savedClip = g2.getClip();
263            g2.setClip(getWaferEdge(plotArea));
264            Rectangle2D chip = new Rectangle2D.Double();
265            int xchips = 35;
266            int ychips = 20;
267            double space = 1d;
268            if (this.dataset != null) {
269                xchips = this.dataset.getMaxChipX() + 2;
270                ychips = this.dataset.getMaxChipY() + 2;
271                space = this.dataset.getChipSpace();
272            }
273            double startX = plotArea.getX();
274            double startY = plotArea.getY();
275            double chipWidth = 1d;
276            double chipHeight = 1d;
277            if (plotArea.getWidth() != plotArea.getHeight()) {
278                double major = 0d;
279                double minor = 0d;
280                if (plotArea.getWidth() > plotArea.getHeight()) {
281                    major = plotArea.getWidth();
282                    minor = plotArea.getHeight();
283                }
284                else {
285                    major = plotArea.getHeight();
286                    minor = plotArea.getWidth();
287                }
288                //set upperLeft point
289                if (plotArea.getWidth() == minor) { // x is minor
290                    startY += (major - minor) / 2;
291                    chipWidth = (plotArea.getWidth() - (space * xchips - 1))
292                        / xchips;
293                    chipHeight = (plotArea.getWidth() - (space * ychips - 1))
294                        / ychips;
295                }
296                else { // y is minor
297                    startX += (major - minor) / 2;
298                    chipWidth = (plotArea.getHeight() - (space * xchips - 1))
299                        / xchips;
300                    chipHeight = (plotArea.getHeight() - (space * ychips - 1))
301                        / ychips;
302                }
303            }
304    
305            for (int x = 1; x <= xchips; x++) {
306                double upperLeftX = (startX - chipWidth) + (chipWidth * x)
307                    + (space * (x - 1));
308                for (int y = 1; y <= ychips; y++) {
309                    double upperLeftY = (startY - chipHeight) + (chipHeight * y)
310                        + (space * (y - 1));
311                    chip.setFrame(upperLeftX, upperLeftY, chipWidth, chipHeight);
312                    g2.setColor(Color.white);
313                    if (this.dataset.getChipValue(x - 1, ychips - y - 1) != null) {
314                        g2.setPaint(
315                            this.renderer.getChipColor(
316                                this.dataset.getChipValue(x - 1, ychips - y - 1)
317                            )
318                        );
319                    }
320                    g2.fill(chip);
321                    g2.setColor(Color.lightGray);
322                    g2.draw(chip);
323                }
324            }
325            g2.setClip(savedClip);
326        }
327    
328        /**
329         * Calculates the location of the waferedge.
330         *
331         * @param plotArea  the plot area.
332         *
333         * @return The wafer edge.
334         */
335        protected Ellipse2D getWaferEdge(Rectangle2D plotArea) {
336            Ellipse2D edge = new Ellipse2D.Double();
337            double diameter = plotArea.getWidth();
338            double upperLeftX = plotArea.getX();
339            double upperLeftY = plotArea.getY();
340            //get major dimension
341            if (plotArea.getWidth() != plotArea.getHeight()) {
342                double major = 0d;
343                double minor = 0d;
344                if (plotArea.getWidth() > plotArea.getHeight()) {
345                    major = plotArea.getWidth();
346                    minor = plotArea.getHeight();
347                }
348                else {
349                    major = plotArea.getHeight();
350                    minor = plotArea.getWidth();
351                }
352                //ellipse diameter is the minor dimension
353                diameter = minor;
354                //set upperLeft point
355                if (plotArea.getWidth() == minor) { // x is minor
356                    upperLeftY = plotArea.getY() + (major - minor) / 2;
357                }
358                else { // y is minor
359                    upperLeftX = plotArea.getX() + (major - minor) / 2;
360                }
361            }
362            edge.setFrame(upperLeftX, upperLeftY, diameter, diameter);
363            return edge;
364        }
365    
366        /**
367         * Draws the waferedge, including the notch.
368         *
369         * @param g2  the graphics device.
370         * @param plotArea  the plot area.
371         */
372        protected void drawWaferEdge(Graphics2D g2, Rectangle2D plotArea) {
373            // draw the wafer
374            Ellipse2D waferEdge = getWaferEdge(plotArea);
375            g2.setColor(Color.black);
376            g2.draw(waferEdge);
377            // calculate and draw the notch
378            // horizontal orientation is considered notch right
379            // vertical orientation is considered notch down
380            Arc2D notch = null;
381            Rectangle2D waferFrame = waferEdge.getFrame();
382            double notchDiameter = waferFrame.getWidth() * 0.04;
383            if (this.orientation == PlotOrientation.HORIZONTAL) {
384                Rectangle2D notchFrame =
385                    new Rectangle2D.Double(
386                        waferFrame.getX() + waferFrame.getWidth()
387                        - (notchDiameter / 2), waferFrame.getY()
388                        + (waferFrame.getHeight() / 2) - (notchDiameter / 2),
389                        notchDiameter, notchDiameter
390                    );
391                notch = new Arc2D.Double(notchFrame, 90d, 180d, Arc2D.OPEN);
392            }
393            else {
394                Rectangle2D notchFrame =
395                    new Rectangle2D.Double(
396                        waferFrame.getX() + (waferFrame.getWidth() / 2)
397                        - (notchDiameter / 2), waferFrame.getY()
398                        + waferFrame.getHeight() - (notchDiameter / 2),
399                        notchDiameter, notchDiameter
400                    );
401                notch = new Arc2D.Double(notchFrame, 0d, 180d, Arc2D.OPEN);
402            }
403            g2.setColor(Color.white);
404            g2.fill(notch);
405            g2.setColor(Color.black);
406            g2.draw(notch);
407    
408        }
409    
410        /**
411         * Return the legend items from the renderer.
412         *
413         * @return The legend items.
414         */
415        public LegendItemCollection getLegendItems() {
416            return this.renderer.getLegendCollection();
417        }
418    
419        /**
420         * Notifies all registered listeners of a renderer change.
421         *
422         * @param event  the event.
423         */
424        public void rendererChanged(RendererChangeEvent event) {
425            fireChangeEvent();
426        }
427    
428    }