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 }