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