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