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