001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ----------------------
028 * StandardDialScale.java
029 * ----------------------
030 * (C) Copyright 2006-2007, 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 *
042 */
043
044 package org.jfree.chart.plot.dial;
045
046 import java.awt.BasicStroke;
047 import java.awt.Color;
048 import java.awt.Font;
049 import java.awt.Graphics2D;
050 import java.awt.Paint;
051 import java.awt.Stroke;
052 import java.awt.geom.Arc2D;
053 import java.awt.geom.Line2D;
054 import java.awt.geom.Point2D;
055 import java.awt.geom.Rectangle2D;
056 import java.io.IOException;
057 import java.io.ObjectInputStream;
058 import java.io.ObjectOutputStream;
059 import java.io.Serializable;
060 import java.text.DecimalFormat;
061 import java.text.NumberFormat;
062
063 import org.jfree.io.SerialUtilities;
064 import org.jfree.text.TextUtilities;
065 import org.jfree.ui.TextAnchor;
066 import org.jfree.util.PaintUtilities;
067 import org.jfree.util.PublicCloneable;
068
069 /**
070 * A scale for a {@link DialPlot}.
071 *
072 * @since 1.0.7
073 */
074 public class StandardDialScale extends AbstractDialLayer implements DialScale,
075 Cloneable, PublicCloneable, Serializable {
076
077 /** For serialization. */
078 static final long serialVersionUID = 3715644629665918516L;
079
080 /** The minimum data value for the scale. */
081 private double lowerBound;
082
083 /** The maximum data value for the scale. */
084 private double upperBound;
085
086 /**
087 * The start angle for the scale display, in degrees (using the same
088 * encoding as Arc2D).
089 */
090 private double startAngle;
091
092 /** The extent of the scale display. */
093 private double extent;
094
095 /**
096 * The factor (in the range 0.0 to 1.0) that determines the outside limit
097 * of the tick marks.
098 */
099 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 }