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 * CompassPlot.java
029 * ----------------
030 * (C) Copyright 2002-2008, by the Australian Antarctic Division and
031 * Contributors.
032 *
033 * Original Author: Bryan Scott (for the Australian Antarctic Division);
034 * Contributor(s): David Gilbert (for Object Refinery Limited);
035 * Arnaud Lelievre;
036 *
037 * Changes:
038 * --------
039 * 25-Sep-2002 : Version 1, contributed by Bryan Scott (DG);
040 * 23-Jan-2003 : Removed one constructor (DG);
041 * 26-Mar-2003 : Implemented Serializable (DG);
042 * 27-Mar-2003 : Changed MeterDataset to ValueDataset (DG);
043 * 21-Aug-2003 : Implemented Cloneable (DG);
044 * 08-Sep-2003 : Added internationalization via use of properties
045 * resourceBundle (RFE 690236) (AL);
046 * 09-Sep-2003 : Changed Color --> Paint (DG);
047 * 15-Sep-2003 : Added null data value check (bug report 805009) (DG);
048 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
049 * 16-Mar-2004 : Added support for revolutionDistance to enable support for
050 * other units than degrees.
051 * 16-Mar-2004 : Enabled LongNeedle to rotate about center.
052 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
053 * 17-Apr-2005 : Fixed bug in clone() method (DG);
054 * 05-May-2005 : Updated draw() method parameters (DG);
055 * 08-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
056 * 16-Jun-2005 : Renamed getData() --> getDatasets() and
057 * addData() --> addDataset() (DG);
058 * ------------- JFREECHART 1.0.x ---------------------------------------------
059 * 20-Mar-2007 : Fixed serialization (DG);
060 * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by
061 * Jess Thrysoee (DG);
062 *
063 */
064
065 package org.jfree.chart.plot;
066
067 import java.awt.BasicStroke;
068 import java.awt.Color;
069 import java.awt.Font;
070 import java.awt.Graphics2D;
071 import java.awt.Paint;
072 import java.awt.Polygon;
073 import java.awt.Stroke;
074 import java.awt.geom.Area;
075 import java.awt.geom.Ellipse2D;
076 import java.awt.geom.Point2D;
077 import java.awt.geom.Rectangle2D;
078 import java.io.IOException;
079 import java.io.ObjectInputStream;
080 import java.io.ObjectOutputStream;
081 import java.io.Serializable;
082 import java.util.Arrays;
083 import java.util.ResourceBundle;
084
085 import org.jfree.chart.LegendItemCollection;
086 import org.jfree.chart.event.PlotChangeEvent;
087 import org.jfree.chart.needle.ArrowNeedle;
088 import org.jfree.chart.needle.LineNeedle;
089 import org.jfree.chart.needle.LongNeedle;
090 import org.jfree.chart.needle.MeterNeedle;
091 import org.jfree.chart.needle.MiddlePinNeedle;
092 import org.jfree.chart.needle.PinNeedle;
093 import org.jfree.chart.needle.PlumNeedle;
094 import org.jfree.chart.needle.PointerNeedle;
095 import org.jfree.chart.needle.ShipNeedle;
096 import org.jfree.chart.needle.WindNeedle;
097 import org.jfree.chart.util.ResourceBundleWrapper;
098 import org.jfree.data.general.DefaultValueDataset;
099 import org.jfree.data.general.ValueDataset;
100 import org.jfree.io.SerialUtilities;
101 import org.jfree.ui.RectangleInsets;
102 import org.jfree.util.ObjectUtilities;
103 import org.jfree.util.PaintUtilities;
104
105 /**
106 * A specialised plot that draws a compass to indicate a direction based on the
107 * value from a {@link ValueDataset}.
108 */
109 public class CompassPlot extends Plot implements Cloneable, Serializable {
110
111 /** For serialization. */
112 private static final long serialVersionUID = 6924382802125527395L;
113
114 /** The default label font. */
115 public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif",
116 Font.BOLD, 10);
117
118 /** A constant for the label type. */
119 public static final int NO_LABELS = 0;
120
121 /** A constant for the label type. */
122 public static final int VALUE_LABELS = 1;
123
124 /** The label type (NO_LABELS, VALUE_LABELS). */
125 private int labelType;
126
127 /** The label font. */
128 private Font labelFont;
129
130 /** A flag that controls whether or not a border is drawn. */
131 private boolean drawBorder = false;
132
133 /** The rose highlight paint. */
134 private transient Paint roseHighlightPaint = Color.black;
135
136 /** The rose paint. */
137 private transient Paint rosePaint = Color.yellow;
138
139 /** The rose center paint. */
140 private transient Paint roseCenterPaint = Color.white;
141
142 /** The compass font. */
143 private Font compassFont = new Font("Arial", Font.PLAIN, 10);
144
145 /** A working shape. */
146 private transient Ellipse2D circle1;
147
148 /** A working shape. */
149 private transient Ellipse2D circle2;
150
151 /** A working area. */
152 private transient Area a1;
153
154 /** A working area. */
155 private transient Area a2;
156
157 /** A working shape. */
158 private transient Rectangle2D rect1;
159
160 /** An array of value datasets. */
161 private ValueDataset[] datasets = new ValueDataset[1];
162
163 /** An array of needles. */
164 private MeterNeedle[] seriesNeedle = new MeterNeedle[1];
165
166 /** The resourceBundle for the localization. */
167 protected static ResourceBundle localizationResources
168 = ResourceBundleWrapper.getBundle(
169 "org.jfree.chart.plot.LocalizationBundle");
170
171 /**
172 * The count to complete one revolution. Can be arbitrarily set
173 * For degrees (the default) it is 360, for radians this is 2*Pi, etc
174 */
175 protected double revolutionDistance = 360;
176
177 /**
178 * Default constructor.
179 */
180 public CompassPlot() {
181 this(new DefaultValueDataset());
182 }
183
184 /**
185 * Constructs a new compass plot.
186 *
187 * @param dataset the dataset for the plot (<code>null</code> permitted).
188 */
189 public CompassPlot(ValueDataset dataset) {
190 super();
191 if (dataset != null) {
192 this.datasets[0] = dataset;
193 dataset.addChangeListener(this);
194 }
195 this.circle1 = new Ellipse2D.Double();
196 this.circle2 = new Ellipse2D.Double();
197 this.rect1 = new Rectangle2D.Double();
198 setSeriesNeedle(0);
199 }
200
201 /**
202 * Returns the label type. Defined by the constants: {@link #NO_LABELS}
203 * and {@link #VALUE_LABELS}.
204 *
205 * @return The label type.
206 *
207 * @see #setLabelType(int)
208 */
209 public int getLabelType() {
210 // FIXME: this attribute is never used - deprecate?
211 return this.labelType;
212 }
213
214 /**
215 * Sets the label type (either {@link #NO_LABELS} or {@link #VALUE_LABELS}.
216 *
217 * @param type the type.
218 *
219 * @see #getLabelType()
220 */
221 public void setLabelType(int type) {
222 // FIXME: this attribute is never used - deprecate?
223 if ((type != NO_LABELS) && (type != VALUE_LABELS)) {
224 throw new IllegalArgumentException(
225 "MeterPlot.setLabelType(int): unrecognised type.");
226 }
227 if (this.labelType != type) {
228 this.labelType = type;
229 fireChangeEvent();
230 }
231 }
232
233 /**
234 * Returns the label font.
235 *
236 * @return The label font.
237 *
238 * @see #setLabelFont(Font)
239 */
240 public Font getLabelFont() {
241 // FIXME: this attribute is not used - deprecate?
242 return this.labelFont;
243 }
244
245 /**
246 * Sets the label font and sends a {@link PlotChangeEvent} to all
247 * registered listeners.
248 *
249 * @param font the new label font.
250 *
251 * @see #getLabelFont()
252 */
253 public void setLabelFont(Font font) {
254 // FIXME: this attribute is not used - deprecate?
255 if (font == null) {
256 throw new IllegalArgumentException("Null 'font' not allowed.");
257 }
258 this.labelFont = font;
259 fireChangeEvent();
260 }
261
262 /**
263 * Returns the paint used to fill the outer circle of the compass.
264 *
265 * @return The paint (never <code>null</code>).
266 *
267 * @see #setRosePaint(Paint)
268 */
269 public Paint getRosePaint() {
270 return this.rosePaint;
271 }
272
273 /**
274 * Sets the paint used to fill the outer circle of the compass,
275 * and sends a {@link PlotChangeEvent} to all registered listeners.
276 *
277 * @param paint the paint (<code>null</code> not permitted).
278 *
279 * @see #getRosePaint()
280 */
281 public void setRosePaint(Paint paint) {
282 if (paint == null) {
283 throw new IllegalArgumentException("Null 'paint' argument.");
284 }
285 this.rosePaint = paint;
286 fireChangeEvent();
287 }
288
289 /**
290 * Returns the paint used to fill the inner background area of the
291 * compass.
292 *
293 * @return The paint (never <code>null</code>).
294 *
295 * @see #setRoseCenterPaint(Paint)
296 */
297 public Paint getRoseCenterPaint() {
298 return this.roseCenterPaint;
299 }
300
301 /**
302 * Sets the paint used to fill the inner background area of the compass,
303 * and sends a {@link PlotChangeEvent} to all registered listeners.
304 *
305 * @param paint the paint (<code>null</code> not permitted).
306 *
307 * @see #getRoseCenterPaint()
308 */
309 public void setRoseCenterPaint(Paint paint) {
310 if (paint == null) {
311 throw new IllegalArgumentException("Null 'paint' argument.");
312 }
313 this.roseCenterPaint = paint;
314 fireChangeEvent();
315 }
316
317 /**
318 * Returns the paint used to draw the circles, symbols and labels on the
319 * compass.
320 *
321 * @return The paint (never <code>null</code>).
322 *
323 * @see #setRoseHighlightPaint(Paint)
324 */
325 public Paint getRoseHighlightPaint() {
326 return this.roseHighlightPaint;
327 }
328
329 /**
330 * Sets the paint used to draw the circles, symbols and labels of the
331 * compass, and sends a {@link PlotChangeEvent} to all registered listeners.
332 *
333 * @param paint the paint (<code>null</code> not permitted).
334 *
335 * @see #getRoseHighlightPaint()
336 */
337 public void setRoseHighlightPaint(Paint paint) {
338 if (paint == null) {
339 throw new IllegalArgumentException("Null 'paint' argument.");
340 }
341 this.roseHighlightPaint = paint;
342 fireChangeEvent();
343 }
344
345 /**
346 * Returns a flag that controls whether or not a border is drawn.
347 *
348 * @return The flag.
349 *
350 * @see #setDrawBorder(boolean)
351 */
352 public boolean getDrawBorder() {
353 return this.drawBorder;
354 }
355
356 /**
357 * Sets a flag that controls whether or not a border is drawn.
358 *
359 * @param status the flag status.
360 *
361 * @see #getDrawBorder()
362 */
363 public void setDrawBorder(boolean status) {
364 this.drawBorder = status;
365 fireChangeEvent();
366 }
367
368 /**
369 * Sets the series paint.
370 *
371 * @param series the series index.
372 * @param paint the paint.
373 *
374 * @see #setSeriesOutlinePaint(int, Paint)
375 */
376 public void setSeriesPaint(int series, Paint paint) {
377 // super.setSeriesPaint(series, paint);
378 if ((series >= 0) && (series < this.seriesNeedle.length)) {
379 this.seriesNeedle[series].setFillPaint(paint);
380 }
381 }
382
383 /**
384 * Sets the series outline paint.
385 *
386 * @param series the series index.
387 * @param p the paint.
388 *
389 * @see #setSeriesPaint(int, Paint)
390 */
391 public void setSeriesOutlinePaint(int series, Paint p) {
392
393 if ((series >= 0) && (series < this.seriesNeedle.length)) {
394 this.seriesNeedle[series].setOutlinePaint(p);
395 }
396
397 }
398
399 /**
400 * Sets the series outline stroke.
401 *
402 * @param series the series index.
403 * @param stroke the stroke.
404 *
405 * @see #setSeriesOutlinePaint(int, Paint)
406 */
407 public void setSeriesOutlineStroke(int series, Stroke stroke) {
408
409 if ((series >= 0) && (series < this.seriesNeedle.length)) {
410 this.seriesNeedle[series].setOutlineStroke(stroke);
411 }
412
413 }
414
415 /**
416 * Sets the needle type.
417 *
418 * @param type the type.
419 *
420 * @see #setSeriesNeedle(int, int)
421 */
422 public void setSeriesNeedle(int type) {
423 setSeriesNeedle(0, type);
424 }
425
426 /**
427 * Sets the needle for a series. The needle type is one of the following:
428 * <ul>
429 * <li>0 = {@link ArrowNeedle};</li>
430 * <li>1 = {@link LineNeedle};</li>
431 * <li>2 = {@link LongNeedle};</li>
432 * <li>3 = {@link PinNeedle};</li>
433 * <li>4 = {@link PlumNeedle};</li>
434 * <li>5 = {@link PointerNeedle};</li>
435 * <li>6 = {@link ShipNeedle};</li>
436 * <li>7 = {@link WindNeedle};</li>
437 * <li>8 = {@link ArrowNeedle};</li>
438 * <li>9 = {@link MiddlePinNeedle};</li>
439 * </ul>
440 * @param index the series index.
441 * @param type the needle type.
442 *
443 * @see #setSeriesNeedle(int)
444 */
445 public void setSeriesNeedle(int index, int type) {
446 switch (type) {
447 case 0:
448 setSeriesNeedle(index, new ArrowNeedle(true));
449 setSeriesPaint(index, Color.red);
450 this.seriesNeedle[index].setHighlightPaint(Color.white);
451 break;
452 case 1:
453 setSeriesNeedle(index, new LineNeedle());
454 break;
455 case 2:
456 MeterNeedle longNeedle = new LongNeedle();
457 longNeedle.setRotateY(0.5);
458 setSeriesNeedle(index, longNeedle);
459 break;
460 case 3:
461 setSeriesNeedle(index, new PinNeedle());
462 break;
463 case 4:
464 setSeriesNeedle(index, new PlumNeedle());
465 break;
466 case 5:
467 setSeriesNeedle(index, new PointerNeedle());
468 break;
469 case 6:
470 setSeriesPaint(index, null);
471 setSeriesOutlineStroke(index, new BasicStroke(3));
472 setSeriesNeedle(index, new ShipNeedle());
473 break;
474 case 7:
475 setSeriesPaint(index, Color.blue);
476 setSeriesNeedle(index, new WindNeedle());
477 break;
478 case 8:
479 setSeriesNeedle(index, new ArrowNeedle(true));
480 break;
481 case 9:
482 setSeriesNeedle(index, new MiddlePinNeedle());
483 break;
484
485 default:
486 throw new IllegalArgumentException("Unrecognised type.");
487 }
488
489 }
490
491 /**
492 * Sets the needle for a series and sends a {@link PlotChangeEvent} to all
493 * registered listeners.
494 *
495 * @param index the series index.
496 * @param needle the needle.
497 */
498 public void setSeriesNeedle(int index, MeterNeedle needle) {
499 if ((needle != null) && (index < this.seriesNeedle.length)) {
500 this.seriesNeedle[index] = needle;
501 }
502 fireChangeEvent();
503 }
504
505 /**
506 * Returns an array of dataset references for the plot.
507 *
508 * @return The dataset for the plot, cast as a ValueDataset.
509 *
510 * @see #addDataset(ValueDataset)
511 */
512 public ValueDataset[] getDatasets() {
513 return this.datasets;
514 }
515
516 /**
517 * Adds a dataset to the compass.
518 *
519 * @param dataset the new dataset (<code>null</code> ignored).
520 *
521 * @see #addDataset(ValueDataset, MeterNeedle)
522 */
523 public void addDataset(ValueDataset dataset) {
524 addDataset(dataset, null);
525 }
526
527 /**
528 * Adds a dataset to the compass.
529 *
530 * @param dataset the new dataset (<code>null</code> ignored).
531 * @param needle the needle (<code>null</code> permitted).
532 */
533 public void addDataset(ValueDataset dataset, MeterNeedle needle) {
534
535 if (dataset != null) {
536 int i = this.datasets.length + 1;
537 ValueDataset[] t = new ValueDataset[i];
538 MeterNeedle[] p = new MeterNeedle[i];
539 i = i - 2;
540 for (; i >= 0; --i) {
541 t[i] = this.datasets[i];
542 p[i] = this.seriesNeedle[i];
543 }
544 i = this.datasets.length;
545 t[i] = dataset;
546 p[i] = ((needle != null) ? needle : p[i - 1]);
547
548 ValueDataset[] a = this.datasets;
549 MeterNeedle[] b = this.seriesNeedle;
550 this.datasets = t;
551 this.seriesNeedle = p;
552
553 for (--i; i >= 0; --i) {
554 a[i] = null;
555 b[i] = null;
556 }
557 dataset.addChangeListener(this);
558 }
559 }
560
561 /**
562 * Draws the plot on a Java 2D graphics device (such as the screen or a
563 * printer).
564 *
565 * @param g2 the graphics device.
566 * @param area the area within which the plot should be drawn.
567 * @param anchor the anchor point (<code>null</code> permitted).
568 * @param parentState the state from the parent plot, if there is one.
569 * @param info collects info about the drawing.
570 */
571 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
572 PlotState parentState,
573 PlotRenderingInfo info) {
574
575 int outerRadius = 0;
576 int innerRadius = 0;
577 int x1, y1, x2, y2;
578 double a;
579
580 if (info != null) {
581 info.setPlotArea(area);
582 }
583
584 // adjust for insets...
585 RectangleInsets insets = getInsets();
586 insets.trim(area);
587
588 // draw the background
589 if (this.drawBorder) {
590 drawBackground(g2, area);
591 }
592
593 int midX = (int) (area.getWidth() / 2);
594 int midY = (int) (area.getHeight() / 2);
595 int radius = midX;
596 if (midY < midX) {
597 radius = midY;
598 }
599 --radius;
600 int diameter = 2 * radius;
601
602 midX += (int) area.getMinX();
603 midY += (int) area.getMinY();
604
605 this.circle1.setFrame(midX - radius, midY - radius, diameter, diameter);
606 this.circle2.setFrame(
607 midX - radius + 15, midY - radius + 15,
608 diameter - 30, diameter - 30
609 );
610 g2.setPaint(this.rosePaint);
611 this.a1 = new Area(this.circle1);
612 this.a2 = new Area(this.circle2);
613 this.a1.subtract(this.a2);
614 g2.fill(this.a1);
615
616 g2.setPaint(this.roseCenterPaint);
617 x1 = diameter - 30;
618 g2.fillOval(midX - radius + 15, midY - radius + 15, x1, x1);
619 g2.setPaint(this.roseHighlightPaint);
620 g2.drawOval(midX - radius, midY - radius, diameter, diameter);
621 x1 = diameter - 20;
622 g2.drawOval(midX - radius + 10, midY - radius + 10, x1, x1);
623 x1 = diameter - 30;
624 g2.drawOval(midX - radius + 15, midY - radius + 15, x1, x1);
625 x1 = diameter - 80;
626 g2.drawOval(midX - radius + 40, midY - radius + 40, x1, x1);
627
628 outerRadius = radius - 20;
629 innerRadius = radius - 32;
630 for (int w = 0; w < 360; w += 15) {
631 a = Math.toRadians(w);
632 x1 = midX - ((int) (Math.sin(a) * innerRadius));
633 x2 = midX - ((int) (Math.sin(a) * outerRadius));
634 y1 = midY - ((int) (Math.cos(a) * innerRadius));
635 y2 = midY - ((int) (Math.cos(a) * outerRadius));
636 g2.drawLine(x1, y1, x2, y2);
637 }
638
639 g2.setPaint(this.roseHighlightPaint);
640 innerRadius = radius - 26;
641 outerRadius = 7;
642 for (int w = 45; w < 360; w += 90) {
643 a = Math.toRadians(w);
644 x1 = midX - ((int) (Math.sin(a) * innerRadius));
645 y1 = midY - ((int) (Math.cos(a) * innerRadius));
646 g2.fillOval(x1 - outerRadius, y1 - outerRadius, 2 * outerRadius,
647 2 * outerRadius);
648 }
649
650 /// Squares
651 for (int w = 0; w < 360; w += 90) {
652 a = Math.toRadians(w);
653 x1 = midX - ((int) (Math.sin(a) * innerRadius));
654 y1 = midY - ((int) (Math.cos(a) * innerRadius));
655
656 Polygon p = new Polygon();
657 p.addPoint(x1 - outerRadius, y1);
658 p.addPoint(x1, y1 + outerRadius);
659 p.addPoint(x1 + outerRadius, y1);
660 p.addPoint(x1, y1 - outerRadius);
661 g2.fillPolygon(p);
662 }
663
664 /// Draw N, S, E, W
665 innerRadius = radius - 42;
666 Font f = getCompassFont(radius);
667 g2.setFont(f);
668 g2.drawString("N", midX - 5, midY - innerRadius + f.getSize());
669 g2.drawString("S", midX - 5, midY + innerRadius - 5);
670 g2.drawString("W", midX - innerRadius + 5, midY + 5);
671 g2.drawString("E", midX + innerRadius - f.getSize(), midY + 5);
672
673 // plot the data (unless the dataset is null)...
674 y1 = radius / 2;
675 x1 = radius / 6;
676 Rectangle2D needleArea = new Rectangle2D.Double(
677 (midX - x1), (midY - y1), (2 * x1), (2 * y1)
678 );
679 int x = this.seriesNeedle.length;
680 int current = 0;
681 double value = 0;
682 int i = (this.datasets.length - 1);
683 for (; i >= 0; --i) {
684 ValueDataset data = this.datasets[i];
685
686 if (data != null && data.getValue() != null) {
687 value = (data.getValue().doubleValue())
688 % this.revolutionDistance;
689 value = value / this.revolutionDistance * 360;
690 current = i % x;
691 this.seriesNeedle[current].draw(g2, needleArea, value);
692 }
693 }
694
695 if (this.drawBorder) {
696 drawOutline(g2, area);
697 }
698
699 }
700
701 /**
702 * Returns a short string describing the type of plot.
703 *
704 * @return A string describing the plot.
705 */
706 public String getPlotType() {
707 return localizationResources.getString("Compass_Plot");
708 }
709
710 /**
711 * Returns the legend items for the plot. For now, no legend is available
712 * - this method returns null.
713 *
714 * @return The legend items.
715 */
716 public LegendItemCollection getLegendItems() {
717 return null;
718 }
719
720 /**
721 * No zooming is implemented for compass plot, so this method is empty.
722 *
723 * @param percent the zoom amount.
724 */
725 public void zoom(double percent) {
726 // no zooming possible
727 }
728
729 /**
730 * Returns the font for the compass, adjusted for the size of the plot.
731 *
732 * @param radius the radius.
733 *
734 * @return The font.
735 */
736 protected Font getCompassFont(int radius) {
737 float fontSize = radius / 10.0f;
738 if (fontSize < 8) {
739 fontSize = 8;
740 }
741 Font newFont = this.compassFont.deriveFont(fontSize);
742 return newFont;
743 }
744
745 /**
746 * Tests an object for equality with this plot.
747 *
748 * @param obj the object (<code>null</code> permitted).
749 *
750 * @return A boolean.
751 */
752 public boolean equals(Object obj) {
753 if (obj == this) {
754 return true;
755 }
756 if (!(obj instanceof CompassPlot)) {
757 return false;
758 }
759 if (!super.equals(obj)) {
760 return false;
761 }
762 CompassPlot that = (CompassPlot) obj;
763 if (this.labelType != that.labelType) {
764 return false;
765 }
766 if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) {
767 return false;
768 }
769 if (this.drawBorder != that.drawBorder) {
770 return false;
771 }
772 if (!PaintUtilities.equal(this.roseHighlightPaint,
773 that.roseHighlightPaint)) {
774 return false;
775 }
776 if (!PaintUtilities.equal(this.rosePaint, that.rosePaint)) {
777 return false;
778 }
779 if (!PaintUtilities.equal(this.roseCenterPaint,
780 that.roseCenterPaint)) {
781 return false;
782 }
783 if (!ObjectUtilities.equal(this.compassFont, that.compassFont)) {
784 return false;
785 }
786 if (!Arrays.equals(this.seriesNeedle, that.seriesNeedle)) {
787 return false;
788 }
789 if (getRevolutionDistance() != that.getRevolutionDistance()) {
790 return false;
791 }
792 return true;
793
794 }
795
796 /**
797 * Returns a clone of the plot.
798 *
799 * @return A clone.
800 *
801 * @throws CloneNotSupportedException this class will not throw this
802 * exception, but subclasses (if any) might.
803 */
804 public Object clone() throws CloneNotSupportedException {
805
806 CompassPlot clone = (CompassPlot) super.clone();
807 if (this.circle1 != null) {
808 clone.circle1 = (Ellipse2D) this.circle1.clone();
809 }
810 if (this.circle2 != null) {
811 clone.circle2 = (Ellipse2D) this.circle2.clone();
812 }
813 if (this.a1 != null) {
814 clone.a1 = (Area) this.a1.clone();
815 }
816 if (this.a2 != null) {
817 clone.a2 = (Area) this.a2.clone();
818 }
819 if (this.rect1 != null) {
820 clone.rect1 = (Rectangle2D) this.rect1.clone();
821 }
822 clone.datasets = (ValueDataset[]) this.datasets.clone();
823 clone.seriesNeedle = (MeterNeedle[]) this.seriesNeedle.clone();
824
825 // clone share data sets => add the clone as listener to the dataset
826 for (int i = 0; i < this.datasets.length; ++i) {
827 if (clone.datasets[i] != null) {
828 clone.datasets[i].addChangeListener(clone);
829 }
830 }
831 return clone;
832
833 }
834
835 /**
836 * Sets the count to complete one revolution. Can be arbitrarily set
837 * For degrees (the default) it is 360, for radians this is 2*Pi, etc
838 *
839 * @param size the count to complete one revolution.
840 *
841 * @see #getRevolutionDistance()
842 */
843 public void setRevolutionDistance(double size) {
844 if (size > 0) {
845 this.revolutionDistance = size;
846 }
847 }
848
849 /**
850 * Gets the count to complete one revolution.
851 *
852 * @return The count to complete one revolution.
853 *
854 * @see #setRevolutionDistance(double)
855 */
856 public double getRevolutionDistance() {
857 return this.revolutionDistance;
858 }
859
860 /**
861 * Provides serialization support.
862 *
863 * @param stream the output stream.
864 *
865 * @throws IOException if there is an I/O error.
866 */
867 private void writeObject(ObjectOutputStream stream) throws IOException {
868 stream.defaultWriteObject();
869 SerialUtilities.writePaint(this.rosePaint, stream);
870 SerialUtilities.writePaint(this.roseCenterPaint, stream);
871 SerialUtilities.writePaint(this.roseHighlightPaint, stream);
872 }
873
874 /**
875 * Provides serialization support.
876 *
877 * @param stream the input stream.
878 *
879 * @throws IOException if there is an I/O error.
880 * @throws ClassNotFoundException if there is a classpath problem.
881 */
882 private void readObject(ObjectInputStream stream)
883 throws IOException, ClassNotFoundException {
884 stream.defaultReadObject();
885 this.rosePaint = SerialUtilities.readPaint(stream);
886 this.roseCenterPaint = SerialUtilities.readPaint(stream);
887 this.roseHighlightPaint = SerialUtilities.readPaint(stream);
888 }
889
890 }