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    }