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    }