1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2013, by Object Refinery Limited and Contributors.
6  *
7  * Project Info:  http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
22  * USA.
23  *
24  * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
25  * Other names may be trademarks of their respective owners.]
26  *
27  * -----------------
28  * WaferMapPlot.java
29  * -----------------
30  *
31  * (C) Copyright 2003-2008, by Robert Redburn and Contributors.
32  *
33  * Original Author:  Robert Redburn;
34  * Contributor(s):   David Gilbert (for Object Refinery Limited);
35  *
36  * Changes
37  * -------
38  * 25-Nov-2003 : Version 1 contributed by Robert Redburn (DG);
39  * 05-May-2005 : Updated draw() method parameters (DG);
40  * 10-Jun-2005 : Changed private --> protected for drawChipGrid(),
41  *               drawWaferEdge() and getWafterEdge() (DG);
42  * 16-Jun-2005 : Added default constructor and setDataset() method (DG);
43  * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by
44  *               Jess Thrysoee (DG);
45  *
46  */
47 
48 package org.jfree.chart.plot;
49 
50 import java.awt.BasicStroke;
51 import java.awt.Color;
52 import java.awt.Graphics2D;
53 import java.awt.Paint;
54 import java.awt.Shape;
55 import java.awt.Stroke;
56 import java.awt.geom.Arc2D;
57 import java.awt.geom.Ellipse2D;
58 import java.awt.geom.Point2D;
59 import java.awt.geom.Rectangle2D;
60 import java.io.Serializable;
61 import java.util.ResourceBundle;
62 
63 import org.jfree.chart.LegendItemCollection;
64 import org.jfree.chart.event.PlotChangeEvent;
65 import org.jfree.chart.event.RendererChangeEvent;
66 import org.jfree.chart.event.RendererChangeListener;
67 import org.jfree.chart.renderer.WaferMapRenderer;
68 import org.jfree.chart.util.ResourceBundleWrapper;
69 import org.jfree.data.general.DatasetChangeEvent;
70 import org.jfree.data.general.WaferMapDataset;
71 import org.jfree.ui.RectangleInsets;
72 
73 /**
74  * A wafer map plot.
75  */
76 public class WaferMapPlot extends Plot implements RendererChangeListener,
77         Cloneable, Serializable {
78 
79     /** For serialization. */
80     private static final long serialVersionUID = 4668320403707308155L;
81 
82     /** The default grid line stroke. */
83     public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
84         BasicStroke.CAP_BUTT,
85         BasicStroke.JOIN_BEVEL,
86         0.0f,
87         new float[] {2.0f, 2.0f},
88         0.0f);
89 
90     /** The default grid line paint. */
91     public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
92 
93     /** The default crosshair visibility. */
94     public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
95 
96     /** The default crosshair stroke. */
97     public static final Stroke DEFAULT_CROSSHAIR_STROKE
98             = DEFAULT_GRIDLINE_STROKE;
99 
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      */
WaferMapPlot()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      */
WaferMapPlot(WaferMapDataset dataset)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      */
WaferMapPlot(WaferMapDataset dataset, WaferMapRenderer renderer)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
getPlotType()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      */
getDataset()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      */
setDataset(WaferMapDataset dataset)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      */
setRenderer(WaferMapRenderer renderer)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
draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState state, PlotRenderingInfo info)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      */
drawChipGrid(Graphics2D g2, Rectangle2D plotArea)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      */
getWaferEdge(Rectangle2D plotArea)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      */
drawWaferEdge(Graphics2D g2, Rectangle2D plotArea)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
getLegendItems()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
rendererChanged(RendererChangeEvent event)430     public void rendererChanged(RendererChangeEvent event) {
431         fireChangeEvent();
432     }
433 
434 }
435