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