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  * DialPlot.java
29  * -------------
30  * (C) Copyright 2006-2013, by Object Refinery Limited.
31  *
32  * Original Author:  David Gilbert (for Object Refinery Limited);
33  * Contributor(s):   -;
34  *
35  * Changes
36  * -------
37  * 03-Nov-2006 : Version 1 (DG);
38  * 08-Mar-2007 : Fix in hashCode() (DG);
39  * 17-Oct-2007 : Fixed listener registration/deregistration bugs (DG);
40  * 24-Oct-2007 : Maintain pointers in their own list, so they can be
41  *               drawn after other layers (DG);
42  * 15-Feb-2008 : Fixed clipping bug (1873160) (DG);
43  * 03-Jul-2013 : Use ParamChecks (DG);
44  *
45  */
46 
47 package org.jfree.chart.plot.dial;
48 
49 import java.awt.Graphics2D;
50 import java.awt.Shape;
51 import java.awt.geom.Point2D;
52 import java.awt.geom.Rectangle2D;
53 import java.io.IOException;
54 import java.io.ObjectInputStream;
55 import java.io.ObjectOutputStream;
56 import java.util.Iterator;
57 import java.util.List;
58 
59 import org.jfree.chart.JFreeChart;
60 import org.jfree.chart.event.PlotChangeEvent;
61 import org.jfree.chart.plot.Plot;
62 import org.jfree.chart.plot.PlotRenderingInfo;
63 import org.jfree.chart.plot.PlotState;
64 import org.jfree.chart.util.ParamChecks;
65 import org.jfree.data.general.DatasetChangeEvent;
66 import org.jfree.data.general.ValueDataset;
67 import org.jfree.util.ObjectList;
68 import org.jfree.util.ObjectUtilities;
69 
70 /**
71  * A dial plot composed of user-definable layers.
72  * The example shown here is generated by the <code>DialDemo2.java</code>
73  * program included in the JFreeChart Demo Collection:
74  * <br><br>
75  * <img src="../../../../../images/DialPlotSample.png"
76  * alt="DialPlotSample.png" />
77  *
78  * @since 1.0.7
79  */
80 public class DialPlot extends Plot implements DialLayerChangeListener {
81 
82     /**
83      * The background layer (optional).
84      */
85     private DialLayer background;
86 
87     /**
88      * The needle cap (optional).
89      */
90     private DialLayer cap;
91 
92     /**
93      * The dial frame.
94      */
95     private DialFrame dialFrame;
96 
97     /**
98      * The dataset(s) for the dial plot.
99      */
100     private ObjectList datasets;
101 
102     /**
103      * The scale(s) for the dial plot.
104      */
105     private ObjectList scales;
106 
107     /** Storage for keys that map datasets to scales. */
108     private ObjectList datasetToScaleMap;
109 
110     /**
111      * The drawing layers for the dial plot.
112      */
113     private List layers;
114 
115     /**
116      * The pointer(s) for the dial.
117      */
118     private List pointers;
119 
120     /**
121      * The x-coordinate for the view window.
122      */
123     private double viewX;
124 
125     /**
126      * The y-coordinate for the view window.
127      */
128     private double viewY;
129 
130     /**
131      * The width of the view window, expressed as a percentage.
132      */
133     private double viewW;
134 
135     /**
136      * The height of the view window, expressed as a percentage.
137      */
138     private double viewH;
139 
140     /**
141      * Creates a new instance of <code>DialPlot</code>.
142      */
DialPlot()143     public DialPlot() {
144         this(null);
145     }
146 
147     /**
148      * Creates a new instance of <code>DialPlot</code>.
149      *
150      * @param dataset  the dataset (<code>null</code> permitted).
151      */
DialPlot(ValueDataset dataset)152     public DialPlot(ValueDataset dataset) {
153         this.background = null;
154         this.cap = null;
155         this.dialFrame = new ArcDialFrame();
156         this.datasets = new ObjectList();
157         if (dataset != null) {
158             setDataset(dataset);
159         }
160         this.scales = new ObjectList();
161         this.datasetToScaleMap = new ObjectList();
162         this.layers = new java.util.ArrayList();
163         this.pointers = new java.util.ArrayList();
164         this.viewX = 0.0;
165         this.viewY = 0.0;
166         this.viewW = 1.0;
167         this.viewH = 1.0;
168     }
169 
170     /**
171      * Returns the background.
172      *
173      * @return The background (possibly <code>null</code>).
174      *
175      * @see #setBackground(DialLayer)
176      */
getBackground()177     public DialLayer getBackground() {
178         return this.background;
179     }
180 
181     /**
182      * Sets the background layer and sends a {@link PlotChangeEvent} to all
183      * registered listeners.
184      *
185      * @param background  the background layer (<code>null</code> permitted).
186      *
187      * @see #getBackground()
188      */
setBackground(DialLayer background)189     public void setBackground(DialLayer background) {
190         if (this.background != null) {
191             this.background.removeChangeListener(this);
192         }
193         this.background = background;
194         if (background != null) {
195             background.addChangeListener(this);
196         }
197         fireChangeEvent();
198     }
199 
200     /**
201      * Returns the cap.
202      *
203      * @return The cap (possibly <code>null</code>).
204      *
205      * @see #setCap(DialLayer)
206      */
getCap()207     public DialLayer getCap() {
208         return this.cap;
209     }
210 
211     /**
212      * Sets the cap and sends a {@link PlotChangeEvent} to all registered
213      * listeners.
214      *
215      * @param cap  the cap (<code>null</code> permitted).
216      *
217      * @see #getCap()
218      */
setCap(DialLayer cap)219     public void setCap(DialLayer cap) {
220         if (this.cap != null) {
221             this.cap.removeChangeListener(this);
222         }
223         this.cap = cap;
224         if (cap != null) {
225             cap.addChangeListener(this);
226         }
227         fireChangeEvent();
228     }
229 
230     /**
231      * Returns the dial's frame.
232      *
233      * @return The dial's frame (never <code>null</code>).
234      *
235      * @see #setDialFrame(DialFrame)
236      */
getDialFrame()237     public DialFrame getDialFrame() {
238         return this.dialFrame;
239     }
240 
241     /**
242      * Sets the dial's frame and sends a {@link PlotChangeEvent} to all
243      * registered listeners.
244      *
245      * @param frame  the frame (<code>null</code> not permitted).
246      *
247      * @see #getDialFrame()
248      */
setDialFrame(DialFrame frame)249     public void setDialFrame(DialFrame frame) {
250         ParamChecks.nullNotPermitted(frame, "frame");
251         this.dialFrame.removeChangeListener(this);
252         this.dialFrame = frame;
253         frame.addChangeListener(this);
254         fireChangeEvent();
255     }
256 
257     /**
258      * Returns the x-coordinate of the viewing rectangle.  This is specified
259      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
260      *
261      * @return The x-coordinate of the viewing rectangle.
262      *
263      * @see #setView(double, double, double, double)
264      */
getViewX()265     public double getViewX() {
266         return this.viewX;
267     }
268 
269     /**
270      * Returns the y-coordinate of the viewing rectangle.  This is specified
271      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
272      *
273      * @return The y-coordinate of the viewing rectangle.
274      *
275      * @see #setView(double, double, double, double)
276      */
getViewY()277     public double getViewY() {
278         return this.viewY;
279     }
280 
281     /**
282      * Returns the width of the viewing rectangle.  This is specified
283      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
284      *
285      * @return The width of the viewing rectangle.
286      *
287      * @see #setView(double, double, double, double)
288      */
getViewWidth()289     public double getViewWidth() {
290         return this.viewW;
291     }
292 
293     /**
294      * Returns the height of the viewing rectangle.  This is specified
295      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
296      *
297      * @return The height of the viewing rectangle.
298      *
299      * @see #setView(double, double, double, double)
300      */
getViewHeight()301     public double getViewHeight() {
302         return this.viewH;
303     }
304 
305     /**
306      * Sets the viewing rectangle, relative to the dial's framing rectangle,
307      * and sends a {@link PlotChangeEvent} to all registered listeners.
308      *
309      * @param x  the x-coordinate (in the range 0.0 to 1.0).
310      * @param y  the y-coordinate (in the range 0.0 to 1.0).
311      * @param w  the width (in the range 0.0 to 1.0).
312      * @param h  the height (in the range 0.0 to 1.0).
313      *
314      * @see #getViewX()
315      * @see #getViewY()
316      * @see #getViewWidth()
317      * @see #getViewHeight()
318      */
setView(double x, double y, double w, double h)319     public void setView(double x, double y, double w, double h) {
320         this.viewX = x;
321         this.viewY = y;
322         this.viewW = w;
323         this.viewH = h;
324         fireChangeEvent();
325     }
326 
327     /**
328      * Adds a layer to the plot and sends a {@link PlotChangeEvent} to all
329      * registered listeners.
330      *
331      * @param layer  the layer (<code>null</code> not permitted).
332      */
addLayer(DialLayer layer)333     public void addLayer(DialLayer layer) {
334         ParamChecks.nullNotPermitted(layer, "layer");
335         this.layers.add(layer);
336         layer.addChangeListener(this);
337         fireChangeEvent();
338     }
339 
340     /**
341      * Returns the index for the specified layer.
342      *
343      * @param layer  the layer (<code>null</code> not permitted).
344      *
345      * @return The layer index.
346      */
getLayerIndex(DialLayer layer)347     public int getLayerIndex(DialLayer layer) {
348         ParamChecks.nullNotPermitted(layer, "layer");
349         return this.layers.indexOf(layer);
350     }
351 
352     /**
353      * Removes the layer at the specified index and sends a
354      * {@link PlotChangeEvent} to all registered listeners.
355      *
356      * @param index  the index.
357      */
removeLayer(int index)358     public void removeLayer(int index) {
359         DialLayer layer = (DialLayer) this.layers.get(index);
360         if (layer != null) {
361             layer.removeChangeListener(this);
362         }
363         this.layers.remove(index);
364         fireChangeEvent();
365     }
366 
367     /**
368      * Removes the specified layer and sends a {@link PlotChangeEvent} to all
369      * registered listeners.
370      *
371      * @param layer  the layer (<code>null</code> not permitted).
372      */
removeLayer(DialLayer layer)373     public void removeLayer(DialLayer layer) {
374         // defer argument checking
375         removeLayer(getLayerIndex(layer));
376     }
377 
378     /**
379      * Adds a pointer to the plot and sends a {@link PlotChangeEvent} to all
380      * registered listeners.
381      *
382      * @param pointer  the pointer (<code>null</code> not permitted).
383      */
addPointer(DialPointer pointer)384     public void addPointer(DialPointer pointer) {
385         ParamChecks.nullNotPermitted(pointer, "pointer");
386         this.pointers.add(pointer);
387         pointer.addChangeListener(this);
388         fireChangeEvent();
389     }
390 
391     /**
392      * Returns the index for the specified pointer.
393      *
394      * @param pointer  the pointer (<code>null</code> not permitted).
395      *
396      * @return The pointer index.
397      */
getPointerIndex(DialPointer pointer)398     public int getPointerIndex(DialPointer pointer) {
399         ParamChecks.nullNotPermitted(pointer, "pointer");
400         return this.pointers.indexOf(pointer);
401     }
402 
403     /**
404      * Removes the pointer at the specified index and sends a
405      * {@link PlotChangeEvent} to all registered listeners.
406      *
407      * @param index  the index.
408      */
removePointer(int index)409     public void removePointer(int index) {
410         DialPointer pointer = (DialPointer) this.pointers.get(index);
411         if (pointer != null) {
412             pointer.removeChangeListener(this);
413         }
414         this.pointers.remove(index);
415         fireChangeEvent();
416     }
417 
418     /**
419      * Removes the specified pointer and sends a {@link PlotChangeEvent} to all
420      * registered listeners.
421      *
422      * @param pointer  the pointer (<code>null</code> not permitted).
423      */
removePointer(DialPointer pointer)424     public void removePointer(DialPointer pointer) {
425         // defer argument checking
426         removeLayer(getPointerIndex(pointer));
427     }
428 
429     /**
430      * Returns the dial pointer that is associated with the specified
431      * dataset, or <code>null</code>.
432      *
433      * @param datasetIndex  the dataset index.
434      *
435      * @return The pointer.
436      */
getPointerForDataset(int datasetIndex)437     public DialPointer getPointerForDataset(int datasetIndex) {
438         DialPointer result = null;
439         Iterator iterator = this.pointers.iterator();
440         while (iterator.hasNext()) {
441             DialPointer p = (DialPointer) iterator.next();
442             if (p.getDatasetIndex() == datasetIndex) {
443                 return p;
444             }
445         }
446         return result;
447     }
448 
449     /**
450      * Returns the primary dataset for the plot.
451      *
452      * @return The primary dataset (possibly <code>null</code>).
453      */
getDataset()454     public ValueDataset getDataset() {
455         return getDataset(0);
456     }
457 
458     /**
459      * Returns the dataset at the given index.
460      *
461      * @param index  the dataset index.
462      *
463      * @return The dataset (possibly <code>null</code>).
464      */
getDataset(int index)465     public ValueDataset getDataset(int index) {
466         ValueDataset result = null;
467         if (this.datasets.size() > index) {
468             result = (ValueDataset) this.datasets.get(index);
469         }
470         return result;
471     }
472 
473     /**
474      * Sets the dataset for the plot, replacing the existing dataset, if there
475      * is one, and sends a {@link PlotChangeEvent} to all registered
476      * listeners.
477      *
478      * @param dataset  the dataset (<code>null</code> permitted).
479      */
setDataset(ValueDataset dataset)480     public void setDataset(ValueDataset dataset) {
481         setDataset(0, dataset);
482     }
483 
484     /**
485      * Sets a dataset for the plot.
486      *
487      * @param index  the dataset index.
488      * @param dataset  the dataset (<code>null</code> permitted).
489      */
setDataset(int index, ValueDataset dataset)490     public void setDataset(int index, ValueDataset dataset) {
491 
492         ValueDataset existing = (ValueDataset) this.datasets.get(index);
493         if (existing != null) {
494             existing.removeChangeListener(this);
495         }
496         this.datasets.set(index, dataset);
497         if (dataset != null) {
498             dataset.addChangeListener(this);
499         }
500 
501         // send a dataset change event to self...
502         DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
503         datasetChanged(event);
504 
505     }
506 
507     /**
508      * Returns the number of datasets.
509      *
510      * @return The number of datasets.
511      */
getDatasetCount()512     public int getDatasetCount() {
513         return this.datasets.size();
514     }
515 
516     /**
517      * Draws the plot.  This method is usually called by the {@link JFreeChart}
518      * instance that manages the plot.
519      *
520      * @param g2  the graphics target.
521      * @param area  the area in which the plot should be drawn.
522      * @param anchor  the anchor point (typically the last point that the
523      *     mouse clicked on, <code>null</code> is permitted).
524      * @param parentState  the state for the parent plot (if any).
525      * @param info  used to collect plot rendering info (<code>null</code>
526      *     permitted).
527      */
528     @Override
draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info)529     public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
530             PlotState parentState, PlotRenderingInfo info) {
531 
532         Shape origClip = g2.getClip();
533         g2.setClip(area);
534 
535         // first, expand the viewing area into a drawing frame
536         Rectangle2D frame = viewToFrame(area);
537 
538         // draw the background if there is one...
539         if (this.background != null && this.background.isVisible()) {
540             if (this.background.isClippedToWindow()) {
541                 Shape savedClip = g2.getClip();
542                 g2.clip(this.dialFrame.getWindow(frame));
543                 this.background.draw(g2, this, frame, area);
544                 g2.setClip(savedClip);
545             }
546             else {
547                 this.background.draw(g2, this, frame, area);
548             }
549         }
550 
551         Iterator iterator = this.layers.iterator();
552         while (iterator.hasNext()) {
553             DialLayer current = (DialLayer) iterator.next();
554             if (current.isVisible()) {
555                 if (current.isClippedToWindow()) {
556                     Shape savedClip = g2.getClip();
557                     g2.clip(this.dialFrame.getWindow(frame));
558                     current.draw(g2, this, frame, area);
559                     g2.setClip(savedClip);
560                 }
561                 else {
562                     current.draw(g2, this, frame, area);
563                 }
564             }
565         }
566 
567         // draw the pointers
568         iterator = this.pointers.iterator();
569         while (iterator.hasNext()) {
570             DialPointer current = (DialPointer) iterator.next();
571             if (current.isVisible()) {
572                 if (current.isClippedToWindow()) {
573                     Shape savedClip = g2.getClip();
574                     g2.clip(this.dialFrame.getWindow(frame));
575                     current.draw(g2, this, frame, area);
576                     g2.setClip(savedClip);
577                 }
578                 else {
579                     current.draw(g2, this, frame, area);
580                 }
581             }
582         }
583 
584         // draw the cap if there is one...
585         if (this.cap != null && this.cap.isVisible()) {
586             if (this.cap.isClippedToWindow()) {
587                 Shape savedClip = g2.getClip();
588                 g2.clip(this.dialFrame.getWindow(frame));
589                 this.cap.draw(g2, this, frame, area);
590                 g2.setClip(savedClip);
591             }
592             else {
593                 this.cap.draw(g2, this, frame, area);
594             }
595         }
596 
597         if (this.dialFrame.isVisible()) {
598             this.dialFrame.draw(g2, this, frame, area);
599         }
600 
601         g2.setClip(origClip);
602 
603     }
604 
605     /**
606      * Returns the frame surrounding the specified view rectangle.
607      *
608      * @param view  the view rectangle (<code>null</code> not permitted).
609      *
610      * @return The frame rectangle.
611      */
viewToFrame(Rectangle2D view)612     private Rectangle2D viewToFrame(Rectangle2D view) {
613         double width = view.getWidth() / this.viewW;
614         double height = view.getHeight() / this.viewH;
615         double x = view.getX() - (width * this.viewX);
616         double y = view.getY() - (height * this.viewY);
617         return new Rectangle2D.Double(x, y, width, height);
618     }
619 
620     /**
621      * Returns the value from the specified dataset.
622      *
623      * @param datasetIndex  the dataset index.
624      *
625      * @return The data value.
626      */
getValue(int datasetIndex)627     public double getValue(int datasetIndex) {
628         double result = Double.NaN;
629         ValueDataset dataset = getDataset(datasetIndex);
630         if (dataset != null) {
631             Number n = dataset.getValue();
632             if (n != null) {
633                 result = n.doubleValue();
634             }
635         }
636         return result;
637     }
638 
639     /**
640      * Adds a dial scale to the plot and sends a {@link PlotChangeEvent} to
641      * all registered listeners.
642      *
643      * @param index  the scale index.
644      * @param scale  the scale (<code>null</code> not permitted).
645      */
addScale(int index, DialScale scale)646     public void addScale(int index, DialScale scale) {
647         ParamChecks.nullNotPermitted(scale, "scale");
648         DialScale existing = (DialScale) this.scales.get(index);
649         if (existing != null) {
650             removeLayer(existing);
651         }
652         this.layers.add(scale);
653         this.scales.set(index, scale);
654         scale.addChangeListener(this);
655         fireChangeEvent();
656     }
657 
658     /**
659      * Returns the scale at the given index.
660      *
661      * @param index  the scale index.
662      *
663      * @return The scale (possibly <code>null</code>).
664      */
getScale(int index)665     public DialScale getScale(int index) {
666         DialScale result = null;
667         if (this.scales.size() > index) {
668             result = (DialScale) this.scales.get(index);
669         }
670         return result;
671     }
672 
673     /**
674      * Maps a dataset to a particular scale.
675      *
676      * @param index  the dataset index (zero-based).
677      * @param scaleIndex  the scale index (zero-based).
678      */
mapDatasetToScale(int index, int scaleIndex)679     public void mapDatasetToScale(int index, int scaleIndex) {
680         this.datasetToScaleMap.set(index, new Integer(scaleIndex));
681         fireChangeEvent();
682     }
683 
684     /**
685      * Returns the dial scale for a specific dataset.
686      *
687      * @param datasetIndex  the dataset index.
688      *
689      * @return The dial scale.
690      */
getScaleForDataset(int datasetIndex)691     public DialScale getScaleForDataset(int datasetIndex) {
692         DialScale result = (DialScale) this.scales.get(0);
693         Integer scaleIndex = (Integer) this.datasetToScaleMap.get(datasetIndex);
694         if (scaleIndex != null) {
695             result = getScale(scaleIndex.intValue());
696         }
697         return result;
698     }
699 
700     /**
701      * A utility method that computes a rectangle using relative radius values.
702      *
703      * @param rect  the reference rectangle (<code>null</code> not permitted).
704      * @param radiusW  the width radius (must be > 0.0)
705      * @param radiusH  the height radius.
706      *
707      * @return A new rectangle.
708      */
rectangleByRadius(Rectangle2D rect, double radiusW, double radiusH)709     public static Rectangle2D rectangleByRadius(Rectangle2D rect,
710             double radiusW, double radiusH) {
711         ParamChecks.nullNotPermitted(rect, "rect");
712         double x = rect.getCenterX();
713         double y = rect.getCenterY();
714         double w = rect.getWidth() * radiusW;
715         double h = rect.getHeight() * radiusH;
716         return new Rectangle2D.Double(x - w / 2.0, y - h / 2.0, w, h);
717     }
718 
719     /**
720      * Receives notification when a layer has changed, and responds by
721      * forwarding a {@link PlotChangeEvent} to all registered listeners.
722      *
723      * @param event  the event.
724      */
725     @Override
dialLayerChanged(DialLayerChangeEvent event)726     public void dialLayerChanged(DialLayerChangeEvent event) {
727         fireChangeEvent();
728     }
729 
730     /**
731      * Tests this <code>DialPlot</code> instance for equality with an
732      * arbitrary object.  The plot's dataset(s) is (are) not included in
733      * the test.
734      *
735      * @param obj  the object (<code>null</code> permitted).
736      *
737      * @return A boolean.
738      */
739     @Override
equals(Object obj)740     public boolean equals(Object obj) {
741         if (obj == this) {
742             return true;
743         }
744         if (!(obj instanceof DialPlot)) {
745             return false;
746         }
747         DialPlot that = (DialPlot) obj;
748         if (!ObjectUtilities.equal(this.background, that.background)) {
749             return false;
750         }
751         if (!ObjectUtilities.equal(this.cap, that.cap)) {
752             return false;
753         }
754         if (!this.dialFrame.equals(that.dialFrame)) {
755             return false;
756         }
757         if (this.viewX != that.viewX) {
758             return false;
759         }
760         if (this.viewY != that.viewY) {
761             return false;
762         }
763         if (this.viewW != that.viewW) {
764             return false;
765         }
766         if (this.viewH != that.viewH) {
767             return false;
768         }
769         if (!this.layers.equals(that.layers)) {
770             return false;
771         }
772         if (!this.pointers.equals(that.pointers)) {
773             return false;
774         }
775         return super.equals(obj);
776     }
777 
778     /**
779      * Returns a hash code for this instance.
780      *
781      * @return The hash code.
782      */
783     @Override
hashCode()784     public int hashCode() {
785         int result = 193;
786         result = 37 * result + ObjectUtilities.hashCode(this.background);
787         result = 37 * result + ObjectUtilities.hashCode(this.cap);
788         result = 37 * result + this.dialFrame.hashCode();
789         long temp = Double.doubleToLongBits(this.viewX);
790         result = 37 * result + (int) (temp ^ (temp >>> 32));
791         temp = Double.doubleToLongBits(this.viewY);
792         result = 37 * result + (int) (temp ^ (temp >>> 32));
793         temp = Double.doubleToLongBits(this.viewW);
794         result = 37 * result + (int) (temp ^ (temp >>> 32));
795         temp = Double.doubleToLongBits(this.viewH);
796         result = 37 * result + (int) (temp ^ (temp >>> 32));
797         return result;
798     }
799 
800     /**
801      * Returns the plot type.
802      *
803      * @return <code>"DialPlot"</code>
804      */
805     @Override
getPlotType()806     public String getPlotType() {
807         return "DialPlot";
808     }
809 
810     /**
811      * Provides serialization support.
812      *
813      * @param stream  the output stream.
814      *
815      * @throws IOException  if there is an I/O error.
816      */
writeObject(ObjectOutputStream stream)817     private void writeObject(ObjectOutputStream stream) throws IOException {
818         stream.defaultWriteObject();
819     }
820 
821     /**
822      * Provides serialization support.
823      *
824      * @param stream  the input stream.
825      *
826      * @throws IOException  if there is an I/O error.
827      * @throws ClassNotFoundException  if there is a classpath problem.
828      */
readObject(ObjectInputStream stream)829     private void readObject(ObjectInputStream stream)
830             throws IOException, ClassNotFoundException {
831         stream.defaultReadObject();
832     }
833 
834 
835 }
836