Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ---------------------- 28: * StandardDialScale.java 29: * ---------------------- 30: * (C) Copyright 2006-2007, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * Changes 36: * ------- 37: * 03-Nov-2006 : Version 1 (DG); 38: * 17-Nov-2006 : Added flags for tick label visibility (DG); 39: * 24-Oct-2007 : Added tick label formatter (DG); 40: * 19-Nov-2007 : Added some missing accessor methods (DG); 41: * 42: */ 43: 44: package org.jfree.chart.plot.dial; 45: 46: import java.awt.BasicStroke; 47: import java.awt.Color; 48: import java.awt.Font; 49: import java.awt.Graphics2D; 50: import java.awt.Paint; 51: import java.awt.Stroke; 52: import java.awt.geom.Arc2D; 53: import java.awt.geom.Line2D; 54: import java.awt.geom.Point2D; 55: import java.awt.geom.Rectangle2D; 56: import java.io.IOException; 57: import java.io.ObjectInputStream; 58: import java.io.ObjectOutputStream; 59: import java.io.Serializable; 60: import java.text.DecimalFormat; 61: import java.text.NumberFormat; 62: 63: import org.jfree.io.SerialUtilities; 64: import org.jfree.text.TextUtilities; 65: import org.jfree.ui.TextAnchor; 66: import org.jfree.util.PaintUtilities; 67: import org.jfree.util.PublicCloneable; 68: 69: /** 70: * A scale for a {@link DialPlot}. 71: * 72: * @since 1.0.7 73: */ 74: public class StandardDialScale extends AbstractDialLayer implements DialScale, 75: Cloneable, PublicCloneable, Serializable { 76: 77: /** For serialization. */ 78: static final long serialVersionUID = 3715644629665918516L; 79: 80: /** The minimum data value for the scale. */ 81: private double lowerBound; 82: 83: /** The maximum data value for the scale. */ 84: private double upperBound; 85: 86: /** 87: * The start angle for the scale display, in degrees (using the same 88: * encoding as Arc2D). 89: */ 90: private double startAngle; 91: 92: /** The extent of the scale display. */ 93: private double extent; 94: 95: /** 96: * The factor (in the range 0.0 to 1.0) that determines the outside limit 97: * of the tick marks. 98: */ 99: 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: }