Source for org.jfree.chart.plot.dial.StandardDialScale

   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: }