001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2011, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ---------------------- 028 * StandardDialScale.java 029 * ---------------------- 030 * (C) Copyright 2006-2010, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 03-Nov-2006 : Version 1 (DG); 038 * 17-Nov-2006 : Added flags for tick label visibility (DG); 039 * 24-Oct-2007 : Added tick label formatter (DG); 040 * 19-Nov-2007 : Added some missing accessor methods (DG); 041 * 27-Feb-2009 : Fixed bug 2617557: tickLabelPaint ignored (DG); 042 * 09-Feb-2010 : Fixed bug 2946521 (DG); 043 * 044 */ 045 046package org.jfree.chart.plot.dial; 047 048import java.awt.BasicStroke; 049import java.awt.Color; 050import java.awt.Font; 051import java.awt.Graphics2D; 052import java.awt.Paint; 053import java.awt.Stroke; 054import java.awt.geom.Arc2D; 055import java.awt.geom.Line2D; 056import java.awt.geom.Point2D; 057import java.awt.geom.Rectangle2D; 058import java.io.IOException; 059import java.io.ObjectInputStream; 060import java.io.ObjectOutputStream; 061import java.io.Serializable; 062import java.text.DecimalFormat; 063import java.text.NumberFormat; 064 065import org.jfree.io.SerialUtilities; 066import org.jfree.text.TextUtilities; 067import org.jfree.ui.TextAnchor; 068import org.jfree.util.PaintUtilities; 069import org.jfree.util.PublicCloneable; 070 071/** 072 * A scale for a {@link DialPlot}. 073 * 074 * @since 1.0.7 075 */ 076public class StandardDialScale extends AbstractDialLayer implements DialScale, 077 Cloneable, PublicCloneable, Serializable { 078 079 /** For serialization. */ 080 static final long serialVersionUID = 3715644629665918516L; 081 082 /** The minimum data value for the scale. */ 083 private double lowerBound; 084 085 /** The maximum data value for the scale. */ 086 private double upperBound; 087 088 /** 089 * The start angle for the scale display, in degrees (using the same 090 * encoding as Arc2D). 091 */ 092 private double startAngle; 093 094 /** The extent of the scale display. */ 095 private double extent; 096 097 /** 098 * The factor (in the range 0.0 to 1.0) that determines the outside limit 099 * of the tick marks. 100 */ 101 private double tickRadius; 102 103 /** 104 * The increment (in data units) between major tick marks. 105 */ 106 private double majorTickIncrement; 107 108 /** 109 * The factor that is subtracted from the tickRadius to determine the 110 * inner point of the major ticks. 111 */ 112 private double majorTickLength; 113 114 /** 115 * The paint to use for major tick marks. This field is transient because 116 * it requires special handling for serialization. 117 */ 118 private transient Paint majorTickPaint; 119 120 /** 121 * The stroke to use for major tick marks. This field is transient because 122 * it requires special handling for serialization. 123 */ 124 private transient Stroke majorTickStroke; 125 126 /** 127 * The number of minor ticks between each major tick. 128 */ 129 private int minorTickCount; 130 131 /** 132 * The factor that is subtracted from the tickRadius to determine the 133 * inner point of the minor ticks. 134 */ 135 private double minorTickLength; 136 137 /** 138 * The paint to use for minor tick marks. This field is transient because 139 * it requires special handling for serialization. 140 */ 141 private transient Paint minorTickPaint; 142 143 /** 144 * The stroke to use for minor tick marks. This field is transient because 145 * it requires special handling for serialization. 146 */ 147 private transient Stroke minorTickStroke; 148 149 /** 150 * The tick label offset. 151 */ 152 private double tickLabelOffset; 153 154 /** 155 * The tick label font. 156 */ 157 private Font tickLabelFont; 158 159 /** 160 * A flag that controls whether or not the tick labels are 161 * displayed. 162 */ 163 private boolean tickLabelsVisible; 164 165 /** 166 * The number formatter for the tick labels. 167 */ 168 private NumberFormat tickLabelFormatter; 169 170 /** 171 * A flag that controls whether or not the first tick label is 172 * displayed. 173 */ 174 private boolean firstTickLabelVisible; 175 176 /** 177 * The tick label paint. This field is transient because it requires 178 * special handling for serialization. 179 */ 180 private transient Paint tickLabelPaint; 181 182 /** 183 * Creates a new instance of DialScale. 184 */ 185 public StandardDialScale() { 186 this(0.0, 100.0, 175, -170, 10.0, 4); 187 } 188 189 /** 190 * Creates a new instance. 191 * 192 * @param lowerBound the lower bound of the scale. 193 * @param upperBound the upper bound of the scale. 194 * @param startAngle the start angle (in degrees, using the same 195 * orientation as Java's <code>Arc2D</code> class). 196 * @param extent the extent (in degrees, counter-clockwise). 197 * @param majorTickIncrement the interval between major tick marks (must 198 * be > 0). 199 * @param minorTickCount the number of minor ticks between major tick 200 * marks. 201 */ 202 public StandardDialScale(double lowerBound, double upperBound, 203 double startAngle, double extent, double majorTickIncrement, 204 int minorTickCount) { 205 if (majorTickIncrement <= 0.0) { 206 throw new IllegalArgumentException( 207 "Requires 'majorTickIncrement' > 0."); 208 } 209 this.startAngle = startAngle; 210 this.extent = extent; 211 this.lowerBound = lowerBound; 212 this.upperBound = upperBound; 213 this.tickRadius = 0.70; 214 this.tickLabelsVisible = true; 215 this.tickLabelFormatter = new DecimalFormat("0.0"); 216 this.firstTickLabelVisible = true; 217 this.tickLabelFont = new Font("Dialog", Font.BOLD, 16); 218 this.tickLabelPaint = Color.blue; 219 this.tickLabelOffset = 0.10; 220 this.majorTickIncrement = majorTickIncrement; 221 this.majorTickLength = 0.04; 222 this.majorTickPaint = Color.black; 223 this.majorTickStroke = new BasicStroke(3.0f); 224 this.minorTickCount = minorTickCount; 225 this.minorTickLength = 0.02; 226 this.minorTickPaint = Color.black; 227 this.minorTickStroke = new BasicStroke(1.0f); 228 } 229 230 /** 231 * Returns the lower bound for the scale. 232 * 233 * @return The lower bound for the scale. 234 * 235 * @see #setLowerBound(double) 236 * 237 * @since 1.0.8 238 */ 239 public double getLowerBound() { 240 return this.lowerBound; 241 } 242 243 /** 244 * Sets the lower bound for the scale and sends a 245 * {@link DialLayerChangeEvent} to all registered listeners. 246 * 247 * @param lower the lower bound. 248 * 249 * @see #getLowerBound() 250 * 251 * @since 1.0.8 252 */ 253 public void setLowerBound(double lower) { 254 this.lowerBound = lower; 255 notifyListeners(new DialLayerChangeEvent(this)); 256 } 257 258 /** 259 * Returns the upper bound for the scale. 260 * 261 * @return The upper bound for the scale. 262 * 263 * @see #setUpperBound(double) 264 * 265 * @since 1.0.8 266 */ 267 public double getUpperBound() { 268 return this.upperBound; 269 } 270 271 /** 272 * Sets the upper bound for the scale and sends a 273 * {@link DialLayerChangeEvent} to all registered listeners. 274 * 275 * @param upper the upper bound. 276 * 277 * @see #getUpperBound() 278 * 279 * @since 1.0.8 280 */ 281 public void setUpperBound(double upper) { 282 this.upperBound = upper; 283 notifyListeners(new DialLayerChangeEvent(this)); 284 } 285 286 /** 287 * Returns the start angle for the scale (in degrees using the same 288 * orientation as Java's <code>Arc2D</code> class). 289 * 290 * @return The start angle. 291 * 292 * @see #setStartAngle(double) 293 */ 294 public double getStartAngle() { 295 return this.startAngle; 296 } 297 298 /** 299 * Sets the start angle for the scale and sends a 300 * {@link DialLayerChangeEvent} to all registered listeners. 301 * 302 * @param angle the angle (in degrees). 303 * 304 * @see #getStartAngle() 305 */ 306 public void setStartAngle(double angle) { 307 this.startAngle = angle; 308 notifyListeners(new DialLayerChangeEvent(this)); 309 } 310 311 /** 312 * Returns the extent. 313 * 314 * @return The extent. 315 * 316 * @see #setExtent(double) 317 */ 318 public double getExtent() { 319 return this.extent; 320 } 321 322 /** 323 * Sets the extent and sends a {@link DialLayerChangeEvent} to all 324 * registered listeners. 325 * 326 * @param extent the extent. 327 * 328 * @see #getExtent() 329 */ 330 public void setExtent(double extent) { 331 this.extent = extent; 332 notifyListeners(new DialLayerChangeEvent(this)); 333 } 334 335 /** 336 * Returns the radius (as a percentage of the maximum space available) of 337 * the outer limit of the tick marks. 338 * 339 * @return The tick radius. 340 * 341 * @see #setTickRadius(double) 342 */ 343 public double getTickRadius() { 344 return this.tickRadius; 345 } 346 347 /** 348 * Sets the tick radius and sends a {@link DialLayerChangeEvent} to all 349 * registered listeners. 350 * 351 * @param radius the radius. 352 * 353 * @see #getTickRadius() 354 */ 355 public void setTickRadius(double radius) { 356 if (radius <= 0.0) { 357 throw new IllegalArgumentException( 358 "The 'radius' must be positive."); 359 } 360 this.tickRadius = radius; 361 notifyListeners(new DialLayerChangeEvent(this)); 362 } 363 364 /** 365 * Returns the increment (in data units) between major tick labels. 366 * 367 * @return The increment between major tick labels. 368 * 369 * @see #setMajorTickIncrement(double) 370 */ 371 public double getMajorTickIncrement() { 372 return this.majorTickIncrement; 373 } 374 375 /** 376 * Sets the increment (in data units) between major tick labels and sends a 377 * {@link DialLayerChangeEvent} to all registered listeners. 378 * 379 * @param increment the increment (must be > 0). 380 * 381 * @see #getMajorTickIncrement() 382 */ 383 public void setMajorTickIncrement(double increment) { 384 if (increment <= 0.0) { 385 throw new IllegalArgumentException( 386 "The 'increment' must be positive."); 387 } 388 this.majorTickIncrement = increment; 389 notifyListeners(new DialLayerChangeEvent(this)); 390 } 391 392 /** 393 * Returns the length factor for the major tick marks. The value is 394 * subtracted from the tick radius to determine the inner starting point 395 * for the tick marks. 396 * 397 * @return The length factor. 398 * 399 * @see #setMajorTickLength(double) 400 */ 401 public double getMajorTickLength() { 402 return this.majorTickLength; 403 } 404 405 /** 406 * Sets the length factor for the major tick marks and sends a 407 * {@link DialLayerChangeEvent} to all registered listeners. 408 * 409 * @param length the length. 410 * 411 * @see #getMajorTickLength() 412 */ 413 public void setMajorTickLength(double length) { 414 if (length < 0.0) { 415 throw new IllegalArgumentException("Negative 'length' argument."); 416 } 417 this.majorTickLength = length; 418 notifyListeners(new DialLayerChangeEvent(this)); 419 } 420 421 /** 422 * Returns the major tick paint. 423 * 424 * @return The major tick paint (never <code>null</code>). 425 * 426 * @see #setMajorTickPaint(Paint) 427 */ 428 public Paint getMajorTickPaint() { 429 return this.majorTickPaint; 430 } 431 432 /** 433 * Sets the major tick paint and sends a {@link DialLayerChangeEvent} to 434 * all registered listeners. 435 * 436 * @param paint the paint (<code>null</code> not permitted). 437 * 438 * @see #getMajorTickPaint() 439 */ 440 public void setMajorTickPaint(Paint paint) { 441 if (paint == null) { 442 throw new IllegalArgumentException("Null 'paint' argument."); 443 } 444 this.majorTickPaint = paint; 445 notifyListeners(new DialLayerChangeEvent(this)); 446 } 447 448 /** 449 * Returns the stroke used to draw the major tick marks. 450 * 451 * @return The stroke (never <code>null</code>). 452 * 453 * @see #setMajorTickStroke(Stroke) 454 */ 455 public Stroke getMajorTickStroke() { 456 return this.majorTickStroke; 457 } 458 459 /** 460 * Sets the stroke used to draw the major tick marks and sends a 461 * {@link DialLayerChangeEvent} to all registered listeners. 462 * 463 * @param stroke the stroke (<code>null</code> not permitted). 464 * 465 * @see #getMajorTickStroke() 466 */ 467 public void setMajorTickStroke(Stroke stroke) { 468 if (stroke == null) { 469 throw new IllegalArgumentException("Null 'stroke' argument."); 470 } 471 this.majorTickStroke = stroke; 472 notifyListeners(new DialLayerChangeEvent(this)); 473 } 474 475 /** 476 * Returns the number of minor tick marks between major tick marks. 477 * 478 * @return The number of minor tick marks between major tick marks. 479 * 480 * @see #setMinorTickCount(int) 481 */ 482 public int getMinorTickCount() { 483 return this.minorTickCount; 484 } 485 486 /** 487 * Sets the number of minor tick marks between major tick marks and sends 488 * a {@link DialLayerChangeEvent} to all registered listeners. 489 * 490 * @param count the count. 491 * 492 * @see #getMinorTickCount() 493 */ 494 public void setMinorTickCount(int count) { 495 if (count < 0) { 496 throw new IllegalArgumentException( 497 "The 'count' cannot be negative."); 498 } 499 this.minorTickCount = count; 500 notifyListeners(new DialLayerChangeEvent(this)); 501 } 502 503 /** 504 * Returns the length factor for the minor tick marks. The value is 505 * subtracted from the tick radius to determine the inner starting point 506 * for the tick marks. 507 * 508 * @return The length factor. 509 * 510 * @see #setMinorTickLength(double) 511 */ 512 public double getMinorTickLength() { 513 return this.minorTickLength; 514 } 515 516 /** 517 * Sets the length factor for the minor tick marks and sends 518 * a {@link DialLayerChangeEvent} to all registered listeners. 519 * 520 * @param length the length. 521 * 522 * @see #getMinorTickLength() 523 */ 524 public void setMinorTickLength(double length) { 525 if (length < 0.0) { 526 throw new IllegalArgumentException("Negative 'length' argument."); 527 } 528 this.minorTickLength = length; 529 notifyListeners(new DialLayerChangeEvent(this)); 530 } 531 532 /** 533 * Returns the paint used to draw the minor tick marks. 534 * 535 * @return The paint (never <code>null</code>). 536 * 537 * @see #setMinorTickPaint(Paint) 538 */ 539 public Paint getMinorTickPaint() { 540 return this.minorTickPaint; 541 } 542 543 /** 544 * Sets the paint used to draw the minor tick marks and sends a 545 * {@link DialLayerChangeEvent} to all registered listeners. 546 * 547 * @param paint the paint (<code>null</code> not permitted). 548 * 549 * @see #getMinorTickPaint() 550 */ 551 public void setMinorTickPaint(Paint paint) { 552 if (paint == null) { 553 throw new IllegalArgumentException("Null 'paint' argument."); 554 } 555 this.minorTickPaint = paint; 556 notifyListeners(new DialLayerChangeEvent(this)); 557 } 558 559 /** 560 * Returns the stroke used to draw the minor tick marks. 561 * 562 * @return The paint (never <code>null</code>). 563 * 564 * @see #setMinorTickStroke(Stroke) 565 * 566 * @since 1.0.8 567 */ 568 public Stroke getMinorTickStroke() { 569 return this.minorTickStroke; 570 } 571 572 /** 573 * Sets the stroke used to draw the minor tick marks and sends a 574 * {@link DialLayerChangeEvent} to all registered listeners. 575 * 576 * @param stroke the stroke (<code>null</code> not permitted). 577 * 578 * @see #getMinorTickStroke() 579 * 580 * @since 1.0.8 581 */ 582 public void setMinorTickStroke(Stroke stroke) { 583 if (stroke == null) { 584 throw new IllegalArgumentException("Null 'stroke' argument."); 585 } 586 this.minorTickStroke = stroke; 587 notifyListeners(new DialLayerChangeEvent(this)); 588 } 589 590 /** 591 * Returns the tick label offset. 592 * 593 * @return The tick label offset. 594 * 595 * @see #setTickLabelOffset(double) 596 */ 597 public double getTickLabelOffset() { 598 return this.tickLabelOffset; 599 } 600 601 /** 602 * Sets the tick label offset and sends a {@link DialLayerChangeEvent} to 603 * all registered listeners. 604 * 605 * @param offset the offset. 606 * 607 * @see #getTickLabelOffset() 608 */ 609 public void setTickLabelOffset(double offset) { 610 this.tickLabelOffset = offset; 611 notifyListeners(new DialLayerChangeEvent(this)); 612 } 613 614 /** 615 * Returns the font used to draw the tick labels. 616 * 617 * @return The font (never <code>null</code>). 618 * 619 * @see #setTickLabelFont(Font) 620 */ 621 public Font getTickLabelFont() { 622 return this.tickLabelFont; 623 } 624 625 /** 626 * Sets the font used to display the tick labels and sends a 627 * {@link DialLayerChangeEvent} to all registered listeners. 628 * 629 * @param font the font (<code>null</code> not permitted). 630 * 631 * @see #getTickLabelFont() 632 */ 633 public void setTickLabelFont(Font font) { 634 if (font == null) { 635 throw new IllegalArgumentException("Null 'font' argument."); 636 } 637 this.tickLabelFont = font; 638 notifyListeners(new DialLayerChangeEvent(this)); 639 } 640 641 /** 642 * Returns the paint used to draw the tick labels. 643 * 644 * @return The paint (<code>null</code> not permitted). 645 * 646 * @see #setTickLabelPaint(Paint) 647 */ 648 public Paint getTickLabelPaint() { 649 return this.tickLabelPaint; 650 } 651 652 /** 653 * Sets the paint used to draw the tick labels and sends a 654 * {@link DialLayerChangeEvent} to all registered listeners. 655 * 656 * @param paint the paint (<code>null</code> not permitted). 657 */ 658 public void setTickLabelPaint(Paint paint) { 659 if (paint == null) { 660 throw new IllegalArgumentException("Null 'paint' argument."); 661 } 662 this.tickLabelPaint = paint; 663 notifyListeners(new DialLayerChangeEvent(this)); 664 } 665 666 /** 667 * Returns <code>true</code> if the tick labels should be displayed, 668 * and <code>false</code> otherwise. 669 * 670 * @return A boolean. 671 * 672 * @see #setTickLabelsVisible(boolean) 673 */ 674 public boolean getTickLabelsVisible() { 675 return this.tickLabelsVisible; 676 } 677 678 /** 679 * Sets the flag that controls whether or not the tick labels are 680 * displayed, and sends a {@link DialLayerChangeEvent} to all registered 681 * listeners. 682 * 683 * @param visible the new flag value. 684 * 685 * @see #getTickLabelsVisible() 686 */ 687 public void setTickLabelsVisible(boolean visible) { 688 this.tickLabelsVisible = visible; 689 notifyListeners(new DialLayerChangeEvent(this)); 690 } 691 692 /** 693 * Returns the number formatter used to convert the tick label values to 694 * strings. 695 * 696 * @return The formatter (never <code>null</code>). 697 * 698 * @see #setTickLabelFormatter(NumberFormat) 699 */ 700 public NumberFormat getTickLabelFormatter() { 701 return this.tickLabelFormatter; 702 } 703 704 /** 705 * Sets the number formatter used to convert the tick label values to 706 * strings, and sends a {@link DialLayerChangeEvent} to all registered 707 * listeners. 708 * 709 * @param formatter the formatter (<code>null</code> not permitted). 710 * 711 * @see #getTickLabelFormatter() 712 */ 713 public void setTickLabelFormatter(NumberFormat formatter) { 714 if (formatter == null) { 715 throw new IllegalArgumentException("Null 'formatter' argument."); 716 } 717 this.tickLabelFormatter = formatter; 718 notifyListeners(new DialLayerChangeEvent(this)); 719 } 720 721 /** 722 * Returns a flag that controls whether or not the first tick label is 723 * visible. 724 * 725 * @return A boolean. 726 * 727 * @see #setFirstTickLabelVisible(boolean) 728 */ 729 public boolean getFirstTickLabelVisible() { 730 return this.firstTickLabelVisible; 731 } 732 733 /** 734 * Sets a flag that controls whether or not the first tick label is 735 * visible, and sends a {@link DialLayerChangeEvent} to all registered 736 * listeners. 737 * 738 * @param visible the new flag value. 739 * 740 * @see #getFirstTickLabelVisible() 741 */ 742 public void setFirstTickLabelVisible(boolean visible) { 743 this.firstTickLabelVisible = visible; 744 notifyListeners(new DialLayerChangeEvent(this)); 745 } 746 747 /** 748 * Returns <code>true</code> to indicate that this layer should be 749 * clipped within the dial window. 750 * 751 * @return <code>true</code>. 752 */ 753 public boolean isClippedToWindow() { 754 return true; 755 } 756 757 /** 758 * Draws the scale on the dial plot. 759 * 760 * @param g2 the graphics target (<code>null</code> not permitted). 761 * @param plot the dial plot (<code>null</code> not permitted). 762 * @param frame the reference frame that is used to construct the 763 * geometry of the plot (<code>null</code> not permitted). 764 * @param view the visible part of the plot (<code>null</code> not 765 * permitted). 766 */ 767 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 768 Rectangle2D view) { 769 770 Rectangle2D arcRect = DialPlot.rectangleByRadius(frame, 771 this.tickRadius, this.tickRadius); 772 Rectangle2D arcRectMajor = DialPlot.rectangleByRadius(frame, 773 this.tickRadius - this.majorTickLength, 774 this.tickRadius - this.majorTickLength); 775 Rectangle2D arcRectMinor = arcRect; 776 if (this.minorTickCount > 0 && this.minorTickLength > 0.0) { 777 arcRectMinor = DialPlot.rectangleByRadius(frame, 778 this.tickRadius - this.minorTickLength, 779 this.tickRadius - this.minorTickLength); 780 } 781 Rectangle2D arcRectForLabels = DialPlot.rectangleByRadius(frame, 782 this.tickRadius - this.tickLabelOffset, 783 this.tickRadius - this.tickLabelOffset); 784 785 boolean firstLabel = true; 786 787 Arc2D arc = new Arc2D.Double(); 788 Line2D workingLine = new Line2D.Double(); 789 for (double v = this.lowerBound; v <= this.upperBound; 790 v += this.majorTickIncrement) { 791 arc.setArc(arcRect, this.startAngle, valueToAngle(v) 792 - this.startAngle, Arc2D.OPEN); 793 Point2D pt0 = arc.getEndPoint(); 794 arc.setArc(arcRectMajor, this.startAngle, valueToAngle(v) 795 - this.startAngle, Arc2D.OPEN); 796 Point2D pt1 = arc.getEndPoint(); 797 g2.setPaint(this.majorTickPaint); 798 g2.setStroke(this.majorTickStroke); 799 workingLine.setLine(pt0, pt1); 800 g2.draw(workingLine); 801 arc.setArc(arcRectForLabels, this.startAngle, valueToAngle(v) 802 - this.startAngle, Arc2D.OPEN); 803 Point2D pt2 = arc.getEndPoint(); 804 805 if (this.tickLabelsVisible) { 806 if (!firstLabel || this.firstTickLabelVisible) { 807 g2.setFont(this.tickLabelFont); 808 g2.setPaint(this.tickLabelPaint); 809 TextUtilities.drawAlignedString( 810 this.tickLabelFormatter.format(v), g2, 811 (float) pt2.getX(), (float) pt2.getY(), 812 TextAnchor.CENTER); 813 } 814 } 815 firstLabel = false; 816 817 // now do the minor tick marks 818 if (this.minorTickCount > 0 && this.minorTickLength > 0.0) { 819 double minorTickIncrement = this.majorTickIncrement 820 / (this.minorTickCount + 1); 821 for (int i = 0; i < this.minorTickCount; i++) { 822 double vv = v + ((i + 1) * minorTickIncrement); 823 if (vv >= this.upperBound) { 824 break; 825 } 826 double angle = valueToAngle(vv); 827 828 arc.setArc(arcRect, this.startAngle, angle 829 - this.startAngle, Arc2D.OPEN); 830 pt0 = arc.getEndPoint(); 831 arc.setArc(arcRectMinor, this.startAngle, angle 832 - this.startAngle, Arc2D.OPEN); 833 Point2D pt3 = arc.getEndPoint(); 834 g2.setStroke(this.minorTickStroke); 835 g2.setPaint(this.minorTickPaint); 836 workingLine.setLine(pt0, pt3); 837 g2.draw(workingLine); 838 } 839 } 840 841 } 842 } 843 844 /** 845 * Converts a data value to an angle against this scale. 846 * 847 * @param value the data value. 848 * 849 * @return The angle (in degrees, using the same specification as Java's 850 * Arc2D class). 851 * 852 * @see #angleToValue(double) 853 */ 854 public double valueToAngle(double value) { 855 double range = this.upperBound - this.lowerBound; 856 double unit = this.extent / range; 857 return this.startAngle + unit * (value - this.lowerBound); 858 } 859 860 /** 861 * Converts the given angle to a data value, based on this scale. 862 * 863 * @param angle the angle. 864 * 865 * @return The data value. 866 * 867 * @see #valueToAngle(double) 868 */ 869 public double angleToValue(double angle) { 870 return Double.NaN; // FIXME 871 } 872 873 /** 874 * Tests this <code>StandardDialScale</code> for equality with an arbitrary 875 * object. 876 * 877 * @param obj the object (<code>null</code> permitted). 878 * 879 * @return A boolean. 880 */ 881 public boolean equals(Object obj) { 882 if (obj == this) { 883 return true; 884 } 885 if (!(obj instanceof StandardDialScale)) { 886 return false; 887 } 888 StandardDialScale that = (StandardDialScale) obj; 889 if (this.lowerBound != that.lowerBound) { 890 return false; 891 } 892 if (this.upperBound != that.upperBound) { 893 return false; 894 } 895 if (this.startAngle != that.startAngle) { 896 return false; 897 } 898 if (this.extent != that.extent) { 899 return false; 900 } 901 if (this.tickRadius != that.tickRadius) { 902 return false; 903 } 904 if (this.majorTickIncrement != that.majorTickIncrement) { 905 return false; 906 } 907 if (this.majorTickLength != that.majorTickLength) { 908 return false; 909 } 910 if (!PaintUtilities.equal(this.majorTickPaint, that.majorTickPaint)) { 911 return false; 912 } 913 if (!this.majorTickStroke.equals(that.majorTickStroke)) { 914 return false; 915 } 916 if (this.minorTickCount != that.minorTickCount) { 917 return false; 918 } 919 if (this.minorTickLength != that.minorTickLength) { 920 return false; 921 } 922 if (!PaintUtilities.equal(this.minorTickPaint, that.minorTickPaint)) { 923 return false; 924 } 925 if (!this.minorTickStroke.equals(that.minorTickStroke)) { 926 return false; 927 } 928 if (this.tickLabelsVisible != that.tickLabelsVisible) { 929 return false; 930 } 931 if (this.tickLabelOffset != that.tickLabelOffset) { 932 return false; 933 } 934 if (!this.tickLabelFont.equals(that.tickLabelFont)) { 935 return false; 936 } 937 if (!PaintUtilities.equal(this.tickLabelPaint, that.tickLabelPaint)) { 938 return false; 939 } 940 return super.equals(obj); 941 } 942 943 /** 944 * Returns a hash code for this instance. 945 * 946 * @return A hash code. 947 */ 948 public int hashCode() { 949 int result = 193; 950 // lowerBound 951 long temp = Double.doubleToLongBits(this.lowerBound); 952 result = 37 * result + (int) (temp ^ (temp >>> 32)); 953 // upperBound 954 temp = Double.doubleToLongBits(this.upperBound); 955 result = 37 * result + (int) (temp ^ (temp >>> 32)); 956 // startAngle 957 temp = Double.doubleToLongBits(this.startAngle); 958 result = 37 * result + (int) (temp ^ (temp >>> 32)); 959 // extent 960 temp = Double.doubleToLongBits(this.extent); 961 result = 37 * result + (int) (temp ^ (temp >>> 32)); 962 // tickRadius 963 temp = Double.doubleToLongBits(this.tickRadius); 964 result = 37 * result + (int) (temp ^ (temp >>> 32)); 965 // majorTickIncrement 966 // majorTickLength 967 // majorTickPaint 968 // majorTickStroke 969 // minorTickCount 970 // minorTickLength 971 // minorTickPaint 972 // minorTickStroke 973 // tickLabelOffset 974 // tickLabelFont 975 // tickLabelsVisible 976 // tickLabelFormatter 977 // firstTickLabelsVisible 978 return result; 979 } 980 981 /** 982 * Returns a clone of this instance. 983 * 984 * @return A clone. 985 * 986 * @throws CloneNotSupportedException if this instance is not cloneable. 987 */ 988 public Object clone() throws CloneNotSupportedException { 989 return super.clone(); 990 } 991 992 /** 993 * Provides serialization support. 994 * 995 * @param stream the output stream. 996 * 997 * @throws IOException if there is an I/O error. 998 */ 999 private void writeObject(ObjectOutputStream stream) throws IOException { 1000 stream.defaultWriteObject(); 1001 SerialUtilities.writePaint(this.majorTickPaint, stream); 1002 SerialUtilities.writeStroke(this.majorTickStroke, stream); 1003 SerialUtilities.writePaint(this.minorTickPaint, stream); 1004 SerialUtilities.writeStroke(this.minorTickStroke, stream); 1005 SerialUtilities.writePaint(this.tickLabelPaint, stream); 1006 } 1007 1008 /** 1009 * Provides serialization support. 1010 * 1011 * @param stream the input stream. 1012 * 1013 * @throws IOException if there is an I/O error. 1014 * @throws ClassNotFoundException if there is a classpath problem. 1015 */ 1016 private void readObject(ObjectInputStream stream) 1017 throws IOException, ClassNotFoundException { 1018 stream.defaultReadObject(); 1019 this.majorTickPaint = SerialUtilities.readPaint(stream); 1020 this.majorTickStroke = SerialUtilities.readStroke(stream); 1021 this.minorTickPaint = SerialUtilities.readPaint(stream); 1022 this.minorTickStroke = SerialUtilities.readStroke(stream); 1023 this.tickLabelPaint = SerialUtilities.readPaint(stream); 1024 } 1025 1026}