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