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