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 }