KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > axis > ValueAxis


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, 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 License
20  * along with this library; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
24  * in the United States and other countries.]
25  *
26  * --------------
27  * ValueAxis.java
28  * --------------
29  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
30  *
31  * Original Author: David Gilbert (for Object Refinery Limited);
32  * Contributor(s): Jonathan Nash;
33  * Nicolas Brodu (for Astrium and EADS Corporate Research
34  * Center);
35  *
36  * $Id: ValueAxis.java,v 1.10 2005/05/19 13:58:11 mungady Exp $
37  *
38  * Changes (from 18-Sep-2001)
39  * --------------------------
40  * 18-Sep-2001 : Added standard header and fixed DOS encoding problem (DG);
41  * 23-Nov-2001 : Overhauled standard tick unit code (DG);
42  * 04-Dec-2001 : Changed constructors to protected, and tidied up default
43  * values (DG);
44  * 12-Dec-2001 : Fixed vertical gridlines bug (DG);
45  * 16-Jan-2002 : Added an optional crosshair, based on the implementation by
46  * Jonathan Nash (DG);
47  * 23-Jan-2002 : Moved the minimum and maximum values to here from NumberAxis,
48  * and changed the type from Number to double (DG);
49  * 25-Feb-2002 : Added default value for autoRange. Changed autoAdjustRange
50  * from public to protected. Updated import statements (DG);
51  * 23-Apr-2002 : Added setRange() method (DG);
52  * 29-Apr-2002 : Added range adjustment methods (DG);
53  * 13-Jun-2002 : Modified setCrosshairValue() to notify listeners only when the
54  * crosshairs are visible, to avoid unnecessary repaints, as
55  * suggested by Kees Kuip (DG);
56  * 25-Jul-2002 : Moved lower and upper margin attributes from the NumberAxis
57  * class (DG);
58  * 05-Sep-2002 : Updated constructor for changes in Axis class (DG);
59  * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
60  * 04-Oct-2002 : Moved standardTickUnits from NumberAxis --> ValueAxis (DG);
61  * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
62  * 19-Nov-2002 : Removed grid settings (now controlled by the plot) (DG);
63  * 27-Nov-2002 : Moved the 'inverted' attributed from NumberAxis to
64  * ValueAxis (DG);
65  * 03-Jan-2003 : Small fix to ensure auto-range minimum is observed
66  * immediately (DG);
67  * 14-Jan-2003 : Changed autoRangeMinimumSize from Number --> double (DG);
68  * 20-Jan-2003 : Replaced monolithic constructor (DG);
69  * 26-Mar-2003 : Implemented Serializable (DG);
70  * 09-May-2003 : Added AxisLocation parameter to translation methods (DG);
71  * 13-Aug-2003 : Implemented Cloneable (DG);
72  * 01-Sep-2003 : Fixed bug 793167 (setMaximumAxisValue exception) (DG);
73  * 02-Sep-2003 : Fixed bug 795366 (zooming on inverted axes) (DG);
74  * 08-Sep-2003 : Completed Serialization support (NB);
75  * 08-Sep-2003 : Renamed get/setMinimumValue --> get/setLowerBound,
76  * and get/setMaximumValue --> get/setUpperBound (DG);
77  * 27-Oct-2003 : Changed DEFAULT_AUTO_RANGE_MINIMUM_SIZE value - see bug ID
78  * 829606 (DG);
79  * 07-Nov-2003 : Changes to tick mechanism (DG);
80  * 06-Jan-2004 : Moved axis line attributes to Axis class (DG);
81  * 21-Jan-2004 : Removed redundant axisLineVisible attribute. Renamed
82  * translateJava2DToValue --> java2DToValue, and
83  * translateValueToJava2D --> valueToJava2D (DG);
84  * 23-Jan-2004 : Fixed setAxisLinePaint() and setAxisLineStroke() which had no
85  * effect (andreas.gawecki@coremedia.com);
86  * 07-Apr-2004 : Changed text bounds calculation (DG);
87  * 26-Apr-2004 : Added getter/setter methods for arrow shapes (DG);
88  * 18-May-2004 : Added methods to set axis range *including* current
89  * margins (DG);
90  * 02-Jun-2004 : Fixed bug in setRangeWithMargins() method (DG);
91  * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities
92  * --> TextUtilities (DG);
93  * 11-Jan-2005 : Removed deprecated methods in preparation for 1.0.0
94  * release (DG);
95  * 21-Apr-2005 : Replaced Insets with RectangleInsets (DG);
96  *
97  */

98
99 package org.jfree.chart.axis;
100
101 import java.awt.Font JavaDoc;
102 import java.awt.FontMetrics JavaDoc;
103 import java.awt.Graphics2D JavaDoc;
104 import java.awt.Polygon JavaDoc;
105 import java.awt.Shape JavaDoc;
106 import java.awt.font.LineMetrics JavaDoc;
107 import java.awt.geom.AffineTransform JavaDoc;
108 import java.awt.geom.Line2D JavaDoc;
109 import java.awt.geom.Rectangle2D JavaDoc;
110 import java.io.IOException JavaDoc;
111 import java.io.ObjectInputStream JavaDoc;
112 import java.io.ObjectOutputStream JavaDoc;
113 import java.io.Serializable JavaDoc;
114 import java.util.Iterator JavaDoc;
115 import java.util.List JavaDoc;
116
117 import org.jfree.chart.event.AxisChangeEvent;
118 import org.jfree.chart.plot.Plot;
119 import org.jfree.data.Range;
120 import org.jfree.io.SerialUtilities;
121 import org.jfree.text.TextUtilities;
122 import org.jfree.ui.RectangleEdge;
123 import org.jfree.ui.RectangleInsets;
124 import org.jfree.util.ObjectUtilities;
125 import org.jfree.util.PublicCloneable;
126
127 /**
128  * The base class for axes that display value data, where values are measured
129  * using the <code>double</code> primitive. The two key subclasses are
130  * {@link DateAxis} and {@link NumberAxis}.
131  */

132 public abstract class ValueAxis extends Axis
133                                 implements Cloneable JavaDoc, PublicCloneable,
134                                            Serializable JavaDoc {
135
136     /** For serialization. */
137     private static final long serialVersionUID = 3698345477322391456L;
138     
139     /** The default axis range. */
140     public static final Range DEFAULT_RANGE = new Range(0.0, 1.0);
141
142     /** The default auto-range value. */
143     public static final boolean DEFAULT_AUTO_RANGE = true;
144
145     /** The default inverted flag setting. */
146     public static final boolean DEFAULT_INVERTED = false;
147
148     /** The default minimum auto range. */
149     public static final double DEFAULT_AUTO_RANGE_MINIMUM_SIZE = 0.00000001;
150
151     /** The default value for the lower margin (0.05 = 5%). */
152     public static final double DEFAULT_LOWER_MARGIN = 0.05;
153
154     /** The default value for the upper margin (0.05 = 5%). */
155     public static final double DEFAULT_UPPER_MARGIN = 0.05;
156
157     /** The default lower bound for the axis. */
158     public static final double DEFAULT_LOWER_BOUND = 0.0;
159
160     /** The default upper bound for the axis. */
161     public static final double DEFAULT_UPPER_BOUND = 1.0;
162
163     /** The default auto-tick-unit-selection value. */
164     public static final boolean DEFAULT_AUTO_TICK_UNIT_SELECTION = true;
165
166     /** The maximum tick count. */
167     public static final int MAXIMUM_TICK_COUNT = 500;
168     
169     /**
170      * A flag that controls whether an arrow is drawn at the positive end of
171      * the axis line.
172      */

173     private boolean positiveArrowVisible;
174     
175     /**
176      * A flag that controls whether an arrow is drawn at the negative end of
177      * the axis line.
178      */

179     private boolean negativeArrowVisible;
180     
181     /** The shape used for an up arrow. */
182     private transient Shape JavaDoc upArrow;
183     
184     /** The shape used for a down arrow. */
185     private transient Shape JavaDoc downArrow;
186     
187     /** The shape used for a left arrow. */
188     private transient Shape JavaDoc leftArrow;
189     
190     /** The shape used for a right arrow. */
191     private transient Shape JavaDoc rightArrow;
192     
193     /** A flag that affects the orientation of the values on the axis. */
194     private boolean inverted;
195
196     /** The axis range. */
197     private Range range;
198
199     /**
200      * Flag that indicates whether the axis automatically scales to fit the
201      * chart data.
202      */

203     private boolean autoRange;
204
205     /** The minimum size for the 'auto' axis range (excluding margins). */
206     private double autoRangeMinimumSize;
207
208     /**
209      * The upper margin percentage. This indicates the amount by which the
210      * maximum axis value exceeds the maximum data value (as a percentage of
211      * the range on the axis) when the axis range is determined automatically.
212      */

213     private double upperMargin;
214
215     /**
216      * The lower margin. This is a percentage that indicates the amount by
217      * which the minimum axis value is "less than" the minimum data value when
218      * the axis range is determined automatically.
219      */

220     private double lowerMargin;
221
222     /**
223      * If this value is positive, the amount is subtracted from the maximum
224      * data value to determine the lower axis range. This can be used to
225      * provide a fixed "window" on dynamic data.
226      */

227     private double fixedAutoRange;
228
229     /**
230      * Flag that indicates whether or not the tick unit is selected
231      * automatically.
232      */

233     private boolean autoTickUnitSelection;
234
235     /** The standard tick units for the axis. */
236     private TickUnitSource standardTickUnits;
237
238     /** An index into an array of standard tick values. */
239     private int autoTickIndex;
240     
241     /** A flag indicating whether or not tick labels are rotated to vertical. */
242     private boolean verticalTickLabels;
243
244     /**
245      * Constructs a value axis.
246      *
247      * @param label the axis label.
248      * @param standardTickUnits the source for standard tick units
249      * (<code>null</code> permitted).
250      */

251     protected ValueAxis(String JavaDoc label, TickUnitSource standardTickUnits) {
252
253         super(label);
254
255         this.positiveArrowVisible = false;
256         this.negativeArrowVisible = false;
257
258         this.range = DEFAULT_RANGE;
259         this.autoRange = DEFAULT_AUTO_RANGE;
260
261         this.inverted = DEFAULT_INVERTED;
262         this.autoRangeMinimumSize = DEFAULT_AUTO_RANGE_MINIMUM_SIZE;
263
264         this.lowerMargin = DEFAULT_LOWER_MARGIN;
265         this.upperMargin = DEFAULT_UPPER_MARGIN;
266
267         this.fixedAutoRange = 0.0;
268
269         this.autoTickUnitSelection = DEFAULT_AUTO_TICK_UNIT_SELECTION;
270         this.standardTickUnits = standardTickUnits;
271         
272         Polygon JavaDoc p1 = new Polygon JavaDoc();
273         p1.addPoint(0, 0);
274         p1.addPoint(-2, 2);
275         p1.addPoint(2, 2);
276         
277         this.upArrow = p1;
278
279         Polygon JavaDoc p2 = new Polygon JavaDoc();
280         p2.addPoint(0, 0);
281         p2.addPoint(-2, -2);
282         p2.addPoint(2, -2);
283
284         this.downArrow = p2;
285
286         Polygon JavaDoc p3 = new Polygon JavaDoc();
287         p3.addPoint(0, 0);
288         p3.addPoint(-2, -2);
289         p3.addPoint(-2, 2);
290         
291         this.rightArrow = p3;
292
293         Polygon JavaDoc p4 = new Polygon JavaDoc();
294         p4.addPoint(0, 0);
295         p4.addPoint(2, -2);
296         p4.addPoint(2, 2);
297
298         this.leftArrow = p4;
299         
300         this.verticalTickLabels = false;
301         
302     }
303
304     /**
305      * Returns <code>true</code> if the tick labels should be rotated (to
306      * vertical), and <code>false</code> otherwise.
307      *
308      * @return <code>true</code> or <code>false</code>.
309      */

310     public boolean isVerticalTickLabels() {
311         return this.verticalTickLabels;
312     }
313
314     /**
315      * Sets the flag that controls whether the tick labels are displayed
316      * vertically (that is, rotated 90 degrees from horizontal). If the flag
317      * is changed, an {@link AxisChangeEvent} is sent to all registered
318      * listeners.
319      *
320      * @param flag the flag.
321      */

322     public void setVerticalTickLabels(boolean flag) {
323         if (this.verticalTickLabels != flag) {
324             this.verticalTickLabels = flag;
325             notifyListeners(new AxisChangeEvent(this));
326         }
327     }
328
329     /**
330      * Returns a flag that controls whether or not the axis line has an arrow
331      * drawn that points in the positive direction for the axis.
332      *
333      * @return A boolean.
334      */

335     public boolean isPositiveArrowVisible() {
336         return this.positiveArrowVisible;
337     }
338     
339     /**
340      * Sets a flag that controls whether or not the axis lines has an arrow
341      * drawn that points in the positive direction for the axis, and sends an
342      * {@link AxisChangeEvent} to all registered listeners.
343      *
344      * @param visible the flag.
345      */

346     public void setPositiveArrowVisible(boolean visible) {
347         this.positiveArrowVisible = visible;
348         notifyListeners(new AxisChangeEvent(this));
349     }
350     
351     /**
352      * Returns a flag that controls whether or not the axis line has an arrow
353      * drawn that points in the negative direction for the axis.
354      *
355      * @return A boolean.
356      */

357     public boolean isNegativeArrowVisible() {
358         return this.negativeArrowVisible;
359     }
360     
361     /**
362      * Sets a flag that controls whether or not the axis lines has an arrow
363      * drawn that points in the negative direction for the axis, and sends an
364      * {@link AxisChangeEvent} to all registered listeners.
365      *
366      * @param visible the flag.
367      */

368     public void setNegativeArrowVisible(boolean visible) {
369         this.negativeArrowVisible = visible;
370         notifyListeners(new AxisChangeEvent(this));
371     }
372     
373     /**
374      * Returns a shape that can be displayed as an arrow pointing upwards at
375      * the end of an axis line.
376      *
377      * @return A shape (never <code>null</code>).
378      */

379     public Shape JavaDoc getUpArrow() {
380         return this.upArrow;
381     }
382     
383     /**
384      * Sets the shape that can be displayed as an arrow pointing upwards at
385      * the end of an axis line and sends an {@link AxisChangeEvent} to all
386      * registered listeners.
387      *
388      * @param arrow the arrow shape (<code>null</code> not permitted).
389      */

390     public void setUpArrow(Shape JavaDoc arrow) {
391         if (arrow == null) {
392             throw new IllegalArgumentException JavaDoc("Null 'arrow' argument.");
393         }
394         this.upArrow = arrow;
395         notifyListeners(new AxisChangeEvent(this));
396     }
397     
398     /**
399      * Returns a shape that can be displayed as an arrow pointing downwards at
400      * the end of an axis line.
401      *
402      * @return A shape (never <code>null</code>).
403      */

404     public Shape JavaDoc getDownArrow() {
405         return this.downArrow;
406     }
407     
408     /**
409      * Sets the shape that can be displayed as an arrow pointing downwards at
410      * the end of an axis line and sends an {@link AxisChangeEvent} to all
411      * registered listeners.
412      *
413      * @param arrow the arrow shape (<code>null</code> not permitted).
414      */

415     public void setDownArrow(Shape JavaDoc arrow) {
416         if (arrow == null) {
417             throw new IllegalArgumentException JavaDoc("Null 'arrow' argument.");
418         }
419         this.downArrow = arrow;
420         notifyListeners(new AxisChangeEvent(this));
421     }
422     
423     /**
424      * Returns a shape that can be displayed as an arrow pointing left at the
425      * end of an axis line.
426      *
427      * @return A shape (never <code>null</code>).
428      */

429     public Shape JavaDoc getLeftArrow() {
430         return this.leftArrow;
431     }
432     
433     /**
434      * Sets the shape that can be displayed as an arrow pointing left at the
435      * end of an axis line and sends an {@link AxisChangeEvent} to all
436      * registered listeners.
437      *
438      * @param arrow the arrow shape (<code>null</code> not permitted).
439      */

440     public void setLeftArrow(Shape JavaDoc arrow) {
441         if (arrow == null) {
442             throw new IllegalArgumentException JavaDoc("Null 'arrow' argument.");
443         }
444         this.leftArrow = arrow;
445         notifyListeners(new AxisChangeEvent(this));
446     }
447     
448     /**
449      * Returns a shape that can be displayed as an arrow pointing right at the
450      * end of an axis line.
451      *
452      * @return A shape (never <code>null</code>).
453      */

454     public Shape JavaDoc getRightArrow() {
455         return this.rightArrow;
456     }
457     
458     /**
459      * Sets the shape that can be displayed as an arrow pointing rightwards at
460      * the end of an axis line and sends an {@link AxisChangeEvent} to all
461      * registered listeners.
462      *
463      * @param arrow the arrow shape (<code>null</code> not permitted).
464      */

465     public void setRightArrow(Shape JavaDoc arrow) {
466         if (arrow == null) {
467             throw new IllegalArgumentException JavaDoc("Null 'arrow' argument.");
468         }
469         this.rightArrow = arrow;
470         notifyListeners(new AxisChangeEvent(this));
471     }
472     
473     /**
474      * Draws an axis line at the current cursor position and edge.
475      *
476      * @param g2 the graphics device.
477      * @param cursor the cursor position.
478      * @param dataArea the data area.
479      * @param edge the edge.
480      */

481     protected void drawAxisLine(Graphics2D JavaDoc g2, double cursor,
482                                 Rectangle2D JavaDoc dataArea, RectangleEdge edge) {
483         Line2D JavaDoc axisLine = null;
484         if (edge == RectangleEdge.TOP) {
485             axisLine = new Line2D.Double JavaDoc(
486                 dataArea.getX(), cursor, dataArea.getMaxX(), cursor
487             );
488         }
489         else if (edge == RectangleEdge.BOTTOM) {
490             axisLine = new Line2D.Double JavaDoc(
491                 dataArea.getX(), cursor, dataArea.getMaxX(), cursor
492             );
493         }
494         else if (edge == RectangleEdge.LEFT) {
495             axisLine = new Line2D.Double JavaDoc(
496                 cursor, dataArea.getY(), cursor, dataArea.getMaxY()
497             );
498         }
499         else if (edge == RectangleEdge.RIGHT) {
500             axisLine = new Line2D.Double JavaDoc(
501                 cursor, dataArea.getY(), cursor, dataArea.getMaxY()
502             );
503         }
504         g2.setPaint(getAxisLinePaint());
505         g2.setStroke(getAxisLineStroke());
506         g2.draw(axisLine);
507         
508         boolean drawUpOrRight = false;
509         boolean drawDownOrLeft = false;
510         if (this.positiveArrowVisible) {
511             if (this.inverted) {
512                 drawDownOrLeft = true;
513             }
514             else {
515                 drawUpOrRight = true;
516             }
517         }
518         if (this.negativeArrowVisible) {
519             if (this.inverted) {
520                 drawUpOrRight = true;
521             }
522             else {
523                 drawDownOrLeft = true;
524             }
525         }
526         if (drawUpOrRight) {
527             double x = 0.0;
528             double y = 0.0;
529             Shape JavaDoc arrow = null;
530             if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
531                 x = dataArea.getMaxX();
532                 y = cursor;
533                 arrow = this.rightArrow;
534             }
535             else if (edge == RectangleEdge.LEFT
536                     || edge == RectangleEdge.RIGHT) {
537                 x = cursor;
538                 y = dataArea.getMinY();
539                 arrow = this.upArrow;
540             }
541
542             // draw the arrow...
543
AffineTransform JavaDoc transformer = new AffineTransform JavaDoc();
544             transformer.setToTranslation(x, y);
545             Shape JavaDoc shape = transformer.createTransformedShape(arrow);
546             g2.fill(shape);
547             g2.draw(shape);
548         }
549         
550         if (drawDownOrLeft) {
551             double x = 0.0;
552             double y = 0.0;
553             Shape JavaDoc arrow = null;
554             if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) {
555                 x = dataArea.getMinX();
556                 y = cursor;
557                 arrow = this.leftArrow;
558             }
559             else if (edge == RectangleEdge.LEFT
560                     || edge == RectangleEdge.RIGHT) {
561                 x = cursor;
562                 y = dataArea.getMaxY();
563                 arrow = this.downArrow;
564             }
565
566             // draw the arrow...
567
AffineTransform JavaDoc transformer = new AffineTransform JavaDoc();
568             transformer.setToTranslation(x, y);
569             Shape JavaDoc shape = transformer.createTransformedShape(arrow);
570             g2.fill(shape);
571             g2.draw(shape);
572         }
573         
574     }
575     
576     /**
577      * Calculates the anchor point for a tick label.
578      *
579      * @param tick the tick.
580      * @param cursor the cursor.
581      * @param dataArea the data area.
582      * @param edge the edge on which the axis is drawn.
583      *
584      * @return The x and y coordinates of the anchor point.
585      */

586     protected float[] calculateAnchorPoint(ValueTick tick,
587                                            double cursor,
588                                            Rectangle2D JavaDoc dataArea,
589                                            RectangleEdge edge) {
590     
591         RectangleInsets insets = getTickLabelInsets();
592         float[] result = new float[2];
593         if (edge == RectangleEdge.TOP) {
594             result[0] = (float) valueToJava2D(tick.getValue(), dataArea, edge);
595             result[1] = (float) (cursor - insets.getBottom() - 2.0);
596         }
597         else if (edge == RectangleEdge.BOTTOM) {
598             result[0] = (float) valueToJava2D(tick.getValue(), dataArea, edge);
599             result[1] = (float) (cursor + insets.getTop() + 2.0);
600         }
601         else if (edge == RectangleEdge.LEFT) {
602             result[0] = (float) (cursor - insets.getLeft() - 2.0);
603             result[1] = (float) valueToJava2D(tick.getValue(), dataArea, edge);
604         }
605         else if (edge == RectangleEdge.RIGHT) {
606             result[0] = (float) (cursor + insets.getRight() + 2.0);
607             result[1] = (float) valueToJava2D(tick.getValue(), dataArea, edge);
608         }
609         return result;
610     }
611     
612     /**
613      * Draws the axis line, tick marks and tick mark labels.
614      *
615      * @param g2 the graphics device.
616      * @param cursor the cursor.
617      * @param plotArea the plot area.
618      * @param dataArea the data area.
619      * @param edge the edge that the axis is aligned with.
620      *
621      * @return The width or height used to draw the axis.
622      */

623     protected AxisState drawTickMarksAndLabels(Graphics2D JavaDoc g2,
624                                                double cursor,
625                                                Rectangle2D JavaDoc plotArea,
626                                                Rectangle2D JavaDoc dataArea,
627                                                RectangleEdge edge) {
628                                               
629         AxisState state = new AxisState(cursor);
630
631         if (isAxisLineVisible()) {
632             drawAxisLine(g2, cursor, dataArea, edge);
633         }
634
635         double ol = getTickMarkOutsideLength();
636         double il = getTickMarkInsideLength();
637
638         List JavaDoc ticks = refreshTicks(g2, state, dataArea, edge);
639         state.setTicks(ticks);
640         g2.setFont(getTickLabelFont());
641         Iterator JavaDoc iterator = ticks.iterator();
642         while (iterator.hasNext()) {
643             ValueTick tick = (ValueTick) iterator.next();
644             if (isTickLabelsVisible()) {
645                 g2.setPaint(getTickLabelPaint());
646                 float[] anchorPoint = calculateAnchorPoint(
647                     tick, cursor, dataArea, edge
648                 );
649                 TextUtilities.drawRotatedString(
650                     tick.getText(), g2,
651                     anchorPoint[0], anchorPoint[1],
652                     tick.getTextAnchor(),
653                     tick.getAngle(),
654                     tick.getRotationAnchor()
655                 );
656             }
657
658             if (isTickMarksVisible()) {
659                 float xx = (float) valueToJava2D(
660                     tick.getValue(), dataArea, edge
661                 );
662                 Line2D JavaDoc mark = null;
663                 g2.setStroke(getTickMarkStroke());
664                 g2.setPaint(getTickMarkPaint());
665                 if (edge == RectangleEdge.LEFT) {
666                     mark = new Line2D.Double JavaDoc(cursor - ol, xx, cursor + il, xx);
667                 }
668                 else if (edge == RectangleEdge.RIGHT) {
669                     mark = new Line2D.Double JavaDoc(cursor + ol, xx, cursor - il, xx);
670                 }
671                 else if (edge == RectangleEdge.TOP) {
672                     mark = new Line2D.Double JavaDoc(xx, cursor - ol, xx, cursor + il);
673                 }
674                 else if (edge == RectangleEdge.BOTTOM) {
675                     mark = new Line2D.Double JavaDoc(xx, cursor + ol, xx, cursor - il);
676                 }
677                 g2.draw(mark);
678             }
679         }
680         
681         // need to work out the space used by the tick labels...
682
// so we can update the cursor...
683
double used = 0.0;
684         if (isTickLabelsVisible()) {
685             if (edge == RectangleEdge.LEFT) {
686                 used += findMaximumTickLabelWidth(
687                     ticks, g2, plotArea, isVerticalTickLabels()
688                 );
689                 state.cursorLeft(used);
690             }
691             else if (edge == RectangleEdge.RIGHT) {
692                 used = findMaximumTickLabelWidth(
693                     ticks, g2, plotArea, isVerticalTickLabels()
694                 );
695                 state.cursorRight(used);
696             }
697             else if (edge == RectangleEdge.TOP) {
698                 used = findMaximumTickLabelHeight(
699                     ticks, g2, plotArea, isVerticalTickLabels()
700                 );
701                 state.cursorUp(used);
702             }
703             else if (edge == RectangleEdge.BOTTOM) {
704                 used = findMaximumTickLabelHeight(
705                     ticks, g2, plotArea, isVerticalTickLabels()
706                 );
707                 state.cursorDown(used);
708             }
709         }
710        
711         return state;
712     }
713     
714     /**
715      * Returns the space required to draw the axis.
716      *
717      * @param g2 the graphics device.
718      * @param plot the plot that the axis belongs to.
719      * @param plotArea the area within which the plot should be drawn.
720      * @param edge the axis location.
721      * @param space the space already reserved (for other axes).
722      *
723      * @return The space required to draw the axis (including pre-reserved
724      * space).
725      */

726     public AxisSpace reserveSpace(Graphics2D JavaDoc g2, Plot plot,
727                                   Rectangle2D JavaDoc plotArea,
728                                   RectangleEdge edge, AxisSpace space) {
729
730         // create a new space object if one wasn't supplied...
731
if (space == null) {
732             space = new AxisSpace();
733         }
734         
735         // if the axis is not visible, no additional space is required...
736
if (!isVisible()) {
737             return space;
738         }
739
740         // if the axis has a fixed dimension, return it...
741
double dimension = getFixedDimension();
742         if (dimension > 0.0) {
743             space.ensureAtLeast(dimension, edge);
744         }
745
746         // calculate the max size of the tick labels (if visible)...
747
double tickLabelHeight = 0.0;
748         double tickLabelWidth = 0.0;
749         if (isTickLabelsVisible()) {
750             g2.setFont(getTickLabelFont());
751             List JavaDoc ticks = refreshTicks(g2, new AxisState(), plotArea, edge);
752             if (RectangleEdge.isTopOrBottom(edge)) {
753                 tickLabelHeight = findMaximumTickLabelHeight(
754                     ticks, g2, plotArea, isVerticalTickLabels()
755                 );
756             }
757             else if (RectangleEdge.isLeftOrRight(edge)) {
758                 tickLabelWidth = findMaximumTickLabelWidth(
759                     ticks, g2, plotArea, isVerticalTickLabels()
760                 );
761             }
762         }
763
764         // get the axis label size and update the space object...
765
Rectangle2D JavaDoc labelEnclosure = getLabelEnclosure(g2, edge);
766         double labelHeight = 0.0;
767         double labelWidth = 0.0;
768         if (RectangleEdge.isTopOrBottom(edge)) {
769             labelHeight = labelEnclosure.getHeight();
770             space.add(labelHeight + tickLabelHeight, edge);
771         }
772         else if (RectangleEdge.isLeftOrRight(edge)) {
773             labelWidth = labelEnclosure.getWidth();
774             space.add(labelWidth + tickLabelWidth, edge);
775         }
776
777         return space;
778
779     }
780
781     /**
782      * A utility method for determining the height of the tallest tick label.
783      *
784      * @param ticks the ticks.
785      * @param g2 the graphics device.
786      * @param drawArea the area within which the plot and axes should be drawn.
787      * @param vertical a flag that indicates whether or not the tick labels
788      * are 'vertical'.
789      *
790      * @return The height of the tallest tick label.
791      */

792     protected double findMaximumTickLabelHeight(List JavaDoc ticks,
793                                                 Graphics2D JavaDoc g2,
794                                                 Rectangle2D JavaDoc drawArea,
795                                                 boolean vertical) {
796                                                     
797         RectangleInsets insets = getTickLabelInsets();
798         Font JavaDoc font = getTickLabelFont();
799         double maxHeight = 0.0;
800         if (vertical) {
801             FontMetrics JavaDoc fm = g2.getFontMetrics(font);
802             Iterator JavaDoc iterator = ticks.iterator();
803             while (iterator.hasNext()) {
804                 Tick tick = (Tick) iterator.next();
805                 Rectangle2D JavaDoc labelBounds = TextUtilities.getTextBounds(
806                     tick.getText(), g2, fm
807                 );
808                 if (labelBounds.getWidth() + insets.getTop()
809                         + insets.getBottom() > maxHeight) {
810                     maxHeight = labelBounds.getWidth()
811                                 + insets.getTop() + insets.getBottom();
812                 }
813             }
814         }
815         else {
816             LineMetrics JavaDoc metrics = font.getLineMetrics(
817                 "ABCxyz", g2.getFontRenderContext()
818             );
819             maxHeight = metrics.getHeight()
820                         + insets.getTop() + insets.getBottom();
821         }
822         return maxHeight;
823         
824     }
825
826     /**
827      * A utility method for determining the width of the widest tick label.
828      *
829      * @param ticks the ticks.
830      * @param g2 the graphics device.
831      * @param drawArea the area within which the plot and axes should be drawn.
832      * @param vertical a flag that indicates whether or not the tick labels
833      * are 'vertical'.
834      *
835      * @return The width of the tallest tick label.
836      */

837     protected double findMaximumTickLabelWidth(List JavaDoc ticks,
838                                                Graphics2D JavaDoc g2,
839                                                Rectangle2D JavaDoc drawArea,
840                                                boolean vertical) {
841                                                    
842         RectangleInsets insets = getTickLabelInsets();
843         Font JavaDoc font = getTickLabelFont();
844         double maxWidth = 0.0;
845         if (!vertical) {
846             FontMetrics JavaDoc fm = g2.getFontMetrics(font);
847             Iterator JavaDoc iterator = ticks.iterator();
848             while (iterator.hasNext()) {
849                 Tick tick = (Tick) iterator.next();
850                 Rectangle2D JavaDoc labelBounds = TextUtilities.getTextBounds(
851                     tick.getText(), g2, fm
852                 );
853                 if (labelBounds.getWidth() + insets.getLeft()
854                         + insets.getRight() > maxWidth) {
855                     maxWidth = labelBounds.getWidth()
856                                + insets.getLeft() + insets.getRight();
857                 }
858             }
859         }
860         else {
861             LineMetrics JavaDoc metrics = font.getLineMetrics(
862                 "ABCxyz", g2.getFontRenderContext()
863             );
864             maxWidth = metrics.getHeight()
865                        + insets.getTop() + insets.getBottom();
866         }
867         return maxWidth;
868         
869     }
870
871     /**
872      * Returns a flag that controls the direction of values on the axis.
873      * <P>
874      * For a regular axis, values increase from left to right (for a horizontal
875      * axis) and bottom to top (for a vertical axis). When the axis is
876      * 'inverted', the values increase in the opposite direction.
877      *
878      * @return The flag.
879      */

880     public boolean isInverted() {
881         return this.inverted;
882     }
883
884     /**
885      * Sets a flag that controls the direction of values on the axis, and
886      * notifies registered listeners that the axis has changed.
887      *
888      * @param flag the flag.
889      */

890     public void setInverted(boolean flag) {
891
892         if (this.inverted != flag) {
893             this.inverted = flag;
894             notifyListeners(new AxisChangeEvent(this));
895         }
896
897     }
898
899     /**
900      * Returns the flag that controls whether or not the axis range is
901      * automatically adjusted to fit the data values.
902      *
903      * @return The flag.
904      */

905     public boolean isAutoRange() {
906         return this.autoRange;
907     }
908
909     /**
910      * Sets a flag that determines whether or not the axis range is
911      * automatically adjusted to fit the data, and notifies registered
912      * listeners that the axis has been modified.
913      *
914      * @param auto the new value of the flag.
915      */

916     public void setAutoRange(boolean auto) {
917         setAutoRange(auto, true);
918     }
919
920     /**
921      * Sets the auto range attribute. If the <code>notify</code> flag is set,
922      * an {@link AxisChangeEvent} is sent to registered listeners.
923      *
924      * @param auto the flag.
925      * @param notify notify listeners?
926      */

927     protected void setAutoRange(boolean auto, boolean notify) {
928         if (this.autoRange != auto) {
929             this.autoRange = auto;
930             if (this.autoRange) {
931                 autoAdjustRange();
932             }
933             if (notify) {
934                 notifyListeners(new AxisChangeEvent(this));
935             }
936         }
937     }
938
939     /**
940      * Returns the minimum size allowed for the axis range when it is
941      * automatically calculated.
942      *
943      * @return The minimum range.
944      */

945     public double getAutoRangeMinimumSize() {
946         return this.autoRangeMinimumSize;
947     }
948
949     /**
950      * Sets the auto range minimum size and sends an {@link AxisChangeEvent}
951      * to all registered listeners.
952      *
953      * @param size the size.
954      */

955     public void setAutoRangeMinimumSize(double size) {
956         setAutoRangeMinimumSize(size, true);
957     }
958
959     /**
960      * Sets the minimum size allowed for the axis range when it is
961      * automatically calculated.
962      * <p>
963      * If requested, an {@link AxisChangeEvent} is forwarded to all registered
964      * listeners.
965      *
966      * @param size the new minimum.
967      * @param notify notify listeners?
968      */

969     public void setAutoRangeMinimumSize(double size, boolean notify) {
970
971         // check argument...
972
if (size <= 0.0) {
973             throw new IllegalArgumentException JavaDoc(
974                 "NumberAxis.setAutoRangeMinimumSize(double): must be > 0.0.");
975         }
976
977         // make the change...
978
if (this.autoRangeMinimumSize != size) {
979             this.autoRangeMinimumSize = size;
980             if (this.autoRange) {
981                 autoAdjustRange();
982             }
983             if (notify) {
984                 notifyListeners(new AxisChangeEvent(this));
985             }
986         }
987
988     }
989
990     /**
991      * Returns the lower margin for the axis, expressed as a percentage of the
992      * axis range. This controls the space added to the lower end of the axis
993      * when the axis range is automatically calculated (it is ignored when the
994      * axis range is set explicitly). The default value is 0.05 (five percent).
995      *
996      * @return The lower margin.
997      */

998     public double getLowerMargin() {
999         return this.lowerMargin;
1000    }
1001
1002    /**
1003     * Sets the lower margin for the axis (as a percentage of the axis range)
1004     * and sends an {@link AxisChangeEvent} to all registered listeners. This
1005     * margin is added only when the axis range is auto-calculated - if you set
1006     * the axis range manually, the margin is ignored.
1007     *
1008     * @param margin the margin percentage (for example, 0.05 is five percent).
1009     */

1010    public void setLowerMargin(double margin) {
1011        this.lowerMargin = margin;
1012        if (isAutoRange()) {
1013            autoAdjustRange();
1014        }
1015        notifyListeners(new AxisChangeEvent(this));
1016    }
1017
1018    /**
1019     * Returns the upper margin for the axis, expressed as a percentage of the
1020     * axis range. This controls the space added to the lower end of the axis
1021     * when the axis range is automatically calculated (it is ignored when the
1022     * axis range is set explicitly). The default value is 0.05 (five percent).
1023     *
1024     * @return The upper margin.
1025     */

1026    public double getUpperMargin() {
1027        return this.upperMargin;
1028    }
1029
1030    /**
1031     * Sets the upper margin for the axis (as a percentage of the axis range)
1032     * and sends an {@link AxisChangeEvent} to all registered listeners. This
1033     * margin is added only when the axis range is auto-calculated - if you set
1034     * the axis range manually, the margin is ignored.
1035     *
1036     * @param margin the margin percentage (for example, 0.05 is five percent).
1037     */

1038    public void setUpperMargin(double margin) {
1039        this.upperMargin = margin;
1040        if (isAutoRange()) {
1041            autoAdjustRange();
1042        }
1043        notifyListeners(new AxisChangeEvent(this));
1044    }
1045
1046    /**
1047     * Returns the fixed auto range.
1048     *
1049     * @return The length.
1050     */

1051    public double getFixedAutoRange() {
1052        return this.fixedAutoRange;
1053    }
1054
1055    /**
1056     * Sets the fixed auto range for the axis.
1057     *
1058     * @param length the range length.
1059     */

1060    public void setFixedAutoRange(double length) {
1061
1062        this.fixedAutoRange = length;
1063        notifyListeners(new AxisChangeEvent(this));
1064
1065    }
1066
1067    /**
1068     * Returns the lower bound of the axis range.
1069     *
1070     * @return The lower bound.
1071     */

1072    public double getLowerBound() {
1073        return this.range.getLowerBound();
1074    }
1075
1076    /**
1077     * Sets the lower bound for the axis range. An {@link AxisChangeEvent} is
1078     * sent to all registered listeners.
1079     *
1080     * @param min the new minimum.
1081     */

1082    public void setLowerBound(double min) {
1083        if (this.range.getUpperBound() > min) {
1084            setRange(new Range(min, this.range.getUpperBound()));
1085        }
1086        else {
1087            setRange(new Range(min, min + 1.0));
1088        }
1089    }
1090
1091    /**
1092     * Returns the upper bound for the axis range.
1093     *
1094     * @return The upper bound.
1095     */

1096    public double getUpperBound() {
1097        return this.range.getUpperBound();
1098    }
1099
1100    /**
1101     * Sets the upper bound for the axis range. An {@link AxisChangeEvent} is
1102     * sent to all registered listeners.
1103     *
1104     * @param max the new maximum.
1105     */

1106    public void setUpperBound(double max) {
1107
1108        if (this.range.getLowerBound() < max) {
1109            setRange(new Range(this.range.getLowerBound(), max));
1110        }
1111        else {
1112            setRange(max - 1.0, max);
1113        }
1114
1115    }
1116
1117    /**
1118     * Returns the range for the axis.
1119     *
1120     * @return The axis range (never <code>null</code>).
1121     */

1122    public Range getRange() {
1123        return this.range;
1124    }
1125
1126    /**
1127     * Sets the range attribute and sends an {@link AxisChangeEvent} to all
1128     * registered listeners. As a side-effect, the auto-range flag is set to
1129     * <code>false</code>.
1130     *
1131     * @param range the range (<code>null</code> not permitted).
1132     */

1133    public void setRange(Range range) {
1134        // defer argument checking
1135
setRange(range, true, true);
1136    }
1137
1138    /**
1139     * Sets the range for the axis, if requested, sends an
1140     * {@link AxisChangeEvent} to all registered listeners. As a side-effect,
1141     * the auto-range flag is set to <code>false</code> (optional).
1142     *
1143     * @param range the range (<code>null</code> not permitted).
1144     * @param turnOffAutoRange a flag that controls whether or not the auto
1145     * range is turned off.
1146     * @param notify a flag that controls whether or not listeners are
1147     * notified.
1148     */

1149    public void setRange(Range range, boolean turnOffAutoRange,
1150                         boolean notify) {
1151        if (range == null) {
1152            throw new IllegalArgumentException JavaDoc("Null 'range' argument.");
1153        }
1154        if (turnOffAutoRange) {
1155            this.autoRange = false;
1156        }
1157        this.range = range;
1158        if (notify) {
1159            notifyListeners(new AxisChangeEvent(this));
1160        }
1161    }
1162
1163    /**
1164     * Sets the axis range and sends an {@link AxisChangeEvent} to all
1165     * registered listeners. As a side-effect, the auto-range flag is set to
1166     * <code>false</code>.
1167     *
1168     * @param lower the lower axis limit.
1169     * @param upper the upper axis limit.
1170     */

1171    public void setRange(double lower, double upper) {
1172        setRange(new Range(lower, upper));
1173    }
1174    
1175    /**
1176     * Sets the range for the axis (after first adding the current margins to
1177     * the specified range) and sends an {@link AxisChangeEvent} to all
1178     * registered listeners.
1179     *
1180     * @param range the range (<code>null</code> not permitted).
1181     */

1182    public void setRangeWithMargins(Range range) {
1183        setRangeWithMargins(range, true, true);
1184    }
1185
1186    /**
1187     * Sets the range for the axis after first adding the current margins to
1188     * the range and, if requested, sends an {@link AxisChangeEvent} to all
1189     * registered listeners. As a side-effect, the auto-range flag is set to
1190     * <code>false</code> (optional).
1191     *
1192     * @param range the range (excluding margins, <code>null</code> not
1193     * permitted).
1194     * @param turnOffAutoRange a flag that controls whether or not the auto
1195     * range is turned off.
1196     * @param notify a flag that controls whether or not listeners are
1197     * notified.
1198     */

1199    public void setRangeWithMargins(Range range, boolean turnOffAutoRange,
1200                                    boolean notify) {
1201        if (range == null) {
1202            throw new IllegalArgumentException JavaDoc("Null 'range' argument.");
1203        }
1204        setRange(
1205            Range.expand(range, getLowerMargin(), getUpperMargin()),
1206            turnOffAutoRange, notify
1207        );
1208    }
1209
1210    /**
1211     * Sets the axis range (after first adding the current margins to the
1212     * range) and sends an {@link AxisChangeEvent} to all registered listeners.
1213     * As a side-effect, the auto-range flag is set to <code>false</code>.
1214     *
1215     * @param lower the lower axis limit.
1216     * @param upper the upper axis limit.
1217     */

1218    public void setRangeWithMargins(double lower, double upper) {
1219        setRangeWithMargins(new Range(lower, upper));
1220    }
1221    
1222    /**
1223     * Sets the axis range, where the new range is 'size' in length, and
1224     * centered on 'value'.
1225     *
1226     * @param value the central value.
1227     * @param length the range length.
1228     */

1229    public void setRangeAboutValue(double value, double length) {
1230        setRange(new Range(value - length / 2, value + length / 2));
1231    }
1232
1233    /**
1234     * Returns a flag indicating whether or not the tick unit is automatically
1235     * selected from a range of standard tick units.
1236     *
1237     * @return A flag indicating whether or not the tick unit is automatically
1238     * selected.
1239     */

1240    public boolean isAutoTickUnitSelection() {
1241        return this.autoTickUnitSelection;
1242    }
1243
1244    /**
1245     * Sets a flag indicating whether or not the tick unit is automatically
1246     * selected from a range of standard tick units. If the flag is changed,
1247     * registered listeners are notified that the chart has changed.
1248     *
1249     * @param flag the new value of the flag.
1250     */

1251    public void setAutoTickUnitSelection(boolean flag) {
1252        setAutoTickUnitSelection(flag, true);
1253    }
1254
1255    /**
1256     * Sets a flag indicating whether or not the tick unit is automatically
1257     * selected from a range of standard tick units.
1258     *
1259     * @param flag the new value of the flag.
1260     * @param notify notify listeners?
1261     */

1262    public void setAutoTickUnitSelection(boolean flag, boolean notify) {
1263
1264        if (this.autoTickUnitSelection != flag) {
1265            this.autoTickUnitSelection = flag;
1266            if (notify) {
1267                notifyListeners(new AxisChangeEvent(this));
1268            }
1269        }
1270    }
1271
1272    /**
1273     * Returns the source for obtaining standard tick units for the axis.
1274     *
1275     * @return The source (possibly <code>null</code>).
1276     */

1277    public TickUnitSource getStandardTickUnits() {
1278        return this.standardTickUnits;
1279    }
1280
1281    /**
1282     * Sets the source for obtaining standard tick units for the axis and sends
1283     * an {@link AxisChangeEvent} to all registered listeners. The axis will
1284     * try to select the smallest tick unit from the source that does not cause
1285     * the tick labels to overlap (see also the
1286     * {@link #setAutoTickUnitSelection(boolean)} method.
1287     *
1288     * @param source the source for standard tick units (<code>null</code>
1289     * permitted).
1290     */

1291    public void setStandardTickUnits(TickUnitSource source) {
1292        this.standardTickUnits = source;
1293        notifyListeners(new AxisChangeEvent(this));
1294    }
1295    
1296    /**
1297     * Converts a data value to a coordinate in Java2D space, assuming that the
1298     * axis runs along one edge of the specified dataArea.
1299     * <p>
1300     * Note that it is possible for the coordinate to fall outside the area.
1301     *
1302     * @param value the data value.
1303     * @param area the area for plotting the data.
1304     * @param edge the edge along which the axis lies.
1305     *
1306     * @return The Java2D coordinate.
1307     */

1308    public abstract double valueToJava2D(double value, Rectangle2D JavaDoc area,
1309                                         RectangleEdge edge);
1310    
1311    /**
1312     * Converts a length in data coordinates into the corresponding length in
1313     * Java2D coordinates.
1314     *
1315     * @param length the length.
1316     * @param area the plot area.
1317     * @param edge the edge along which the axis lies.
1318     *
1319     * @return The length in Java2D coordinates.
1320     */

1321    public double lengthToJava2D(double length, Rectangle2D JavaDoc area,
1322                                 RectangleEdge edge) {
1323        double zero = valueToJava2D(0.0, area, edge);
1324        double l = valueToJava2D(length, area, edge);
1325        return Math.abs(l - zero);
1326    }
1327
1328    /**
1329     * Converts a coordinate in Java2D space to the corresponding data value,
1330     * assuming that the axis runs along one edge of the specified dataArea.
1331     *
1332     * @param java2DValue the coordinate in Java2D space.
1333     * @param area the area in which the data is plotted.
1334     * @param edge the edge along which the axis lies.
1335     *
1336     * @return The data value.
1337     */

1338    public abstract double java2DToValue(double java2DValue,
1339                                         Rectangle2D JavaDoc area,
1340                                         RectangleEdge edge);
1341
1342    /**
1343     * Automatically sets the axis range to fit the range of values in the
1344     * dataset. Sometimes this can depend on the renderer used as well (for
1345     * example, the renderer may "stack" values, requiring an axis range
1346     * greater than otherwise necessary).
1347     */

1348    protected abstract void autoAdjustRange();
1349
1350    /**
1351     * Centers the axis range about the specified value and sends an
1352     * {@link AxisChangeEvent} to all registered listeners.
1353     *
1354     * @param value the center value.
1355     */

1356    public void centerRange(double value) {
1357
1358        double central = this.range.getCentralValue();
1359        Range adjusted = new Range(
1360            this.range.getLowerBound() + value - central,
1361            this.range.getUpperBound() + value - central
1362        );
1363        setRange(adjusted);
1364
1365    }
1366
1367    /**
1368     * Increases or decreases the axis range by the specified percentage about
1369     * the central value and sends an {@link AxisChangeEvent} to all registered
1370     * listeners.
1371     * <P>
1372     * To double the length of the axis range, use 200% (2.0).
1373     * To halve the length of the axis range, use 50% (0.5).
1374     *
1375     * @param percent the resize factor.
1376     */

1377    public void resizeRange(double percent) {
1378        resizeRange(percent, this.range.getCentralValue());
1379    }
1380
1381    /**
1382     * Increases or decreases the axis range by the specified percentage about
1383     * the specified anchor value and sends an {@link AxisChangeEvent} to all
1384     * registered listeners.
1385     * <P>
1386     * To double the length of the axis range, use 200% (2.0).
1387     * To halve the length of the axis range, use 50% (0.5).
1388     *
1389     * @param percent the resize factor.
1390     * @param anchorValue the new central value after the resize.
1391     */

1392    public void resizeRange(double percent, double anchorValue) {
1393
1394        if (percent > 0.0) {
1395            double halfLength = this.range.getLength() * percent / 2;
1396            Range adjusted = new Range(
1397                anchorValue - halfLength, anchorValue + halfLength
1398            );
1399            setRange(adjusted);
1400        }
1401        else {
1402            setAutoRange(true);
1403        }
1404
1405    }
1406    
1407    /**
1408     * Zooms in on the current range.
1409     *
1410     * @param lowerPercent the new lower bound.
1411     * @param upperPercent the new upper bound.
1412     */

1413    public void zoomRange(double lowerPercent, double upperPercent) {
1414        double start = this.range.getLowerBound();
1415        double length = this.range.getLength();
1416        Range adjusted = null;
1417        if (isInverted()) {
1418            adjusted = new Range(start + (length * (1 - upperPercent)),
1419                                 start + (length * (1 - lowerPercent)));
1420        }
1421        else {
1422            adjusted = new Range(
1423                start + length * lowerPercent, start + length * upperPercent
1424            );
1425        }
1426        setRange(adjusted);
1427    }
1428
1429    /**
1430     * Returns the auto tick index.
1431     *
1432     * @return The auto tick index.
1433     */

1434    protected int getAutoTickIndex() {
1435        return this.autoTickIndex;
1436    }
1437
1438    /**
1439     * Sets the auto tick index.
1440     *
1441     * @param index the new value.
1442     */

1443    protected void setAutoTickIndex(int index) {
1444        this.autoTickIndex = index;
1445    }
1446
1447    /**
1448     * Tests the axis for equality with an arbitrary object.
1449     *
1450     * @param obj the object (<code>null</code> permitted).
1451     *
1452     * @return <code>true</code> or <code>false</code>.
1453     */

1454    public boolean equals(Object JavaDoc obj) {
1455
1456        if (obj == this) {
1457            return true;
1458        }
1459
1460        if (!(obj instanceof ValueAxis)) {
1461            return false;
1462        }
1463        if (!super.equals(obj)) {
1464            return false;
1465        }
1466        ValueAxis that = (ValueAxis) obj;
1467
1468        
1469        if (this.positiveArrowVisible != that.positiveArrowVisible) {
1470            return false;
1471        }
1472        if (this.negativeArrowVisible != that.negativeArrowVisible) {
1473            return false;
1474        }
1475        if (this.inverted != that.inverted) {
1476            return false;
1477        }
1478        if (!ObjectUtilities.equal(this.range, that.range)) {
1479            return false;
1480        }
1481        if (this.autoRange != that.autoRange) {
1482            return false;
1483        }
1484        if (this.autoRangeMinimumSize != that.autoRangeMinimumSize) {
1485            return false;
1486        }
1487        if (this.upperMargin != that.upperMargin) {
1488            return false;
1489        }
1490        if (this.lowerMargin != that.lowerMargin) {
1491            return false;
1492        }
1493        if (this.fixedAutoRange != that.fixedAutoRange) {
1494            return false;
1495        }
1496        if (this.autoTickUnitSelection != that.autoTickUnitSelection) {
1497            return false;
1498        }
1499        if (!ObjectUtilities.equal(this.standardTickUnits,
1500                that.standardTickUnits)) {
1501            return false;
1502        }
1503        if (this.verticalTickLabels != that.verticalTickLabels) {
1504            return false;
1505        }
1506
1507        return true;
1508
1509    }
1510    
1511    /**
1512     * Returns a clone of the object.
1513     *
1514     * @return A clone.
1515     *
1516     * @throws CloneNotSupportedException if some component of the axis does
1517     * not support cloning.
1518     */

1519    public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
1520        ValueAxis clone = (ValueAxis) super.clone();
1521        return clone;
1522    }
1523    
1524    /**
1525     * Provides serialization support.
1526     *
1527     * @param stream the output stream.
1528     *
1529     * @throws IOException if there is an I/O error.
1530     */

1531    private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
1532
1533        stream.defaultWriteObject();
1534        SerialUtilities.writeShape(this.upArrow, stream);
1535        SerialUtilities.writeShape(this.downArrow, stream);
1536        SerialUtilities.writeShape(this.leftArrow, stream);
1537        SerialUtilities.writeShape(this.rightArrow, stream);
1538
1539    }
1540
1541    /**
1542     * Provides serialization support.
1543     *
1544     * @param stream the input stream.
1545     *
1546     * @throws IOException if there is an I/O error.
1547     * @throws ClassNotFoundException if there is a classpath problem.
1548     */

1549    private void readObject(ObjectInputStream JavaDoc stream)
1550        throws IOException JavaDoc, ClassNotFoundException JavaDoc {
1551
1552        stream.defaultReadObject();
1553        this.upArrow = SerialUtilities.readShape(stream);
1554        this.downArrow = SerialUtilities.readShape(stream);
1555        this.leftArrow = SerialUtilities.readShape(stream);
1556        this.rightArrow = SerialUtilities.readShape(stream);
1557
1558    }
1559   
1560}
1561
Popular Tags