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