KickJava   Java API By Example, From Geeks To Geeks.

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


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  * LogarithmicAxis.java
28  * --------------------
29  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
30  *
31  * Original Author: Michael Duffy / Eric Thomas;
32  * Contributor(s): David Gilbert (for Object Refinery Limited);
33  * David M. O'Donnell;
34  * Scott Sams;
35  *
36  * $Id: LogarithmicAxis.java,v 1.11 2005/05/19 13:58:11 mungady Exp $
37  *
38  * Changes
39  * -------
40  * 14-Mar-2002 : Version 1 contributed by Michael Duffy (DG);
41  * 19-Apr-2002 : drawVerticalString() is now drawRotatedString() in
42  * RefineryUtilities (DG);
43  * 23-Apr-2002 : Added a range property (DG);
44  * 15-May-2002 : Modified to be able to deal with negative and zero values (via
45  * new 'adjustedLog10()' method); occurrences of "Math.log(10)"
46  * changed to "LOG10_VALUE"; changed 'intValue()' to
47  * 'longValue()' in 'refreshTicks()' to fix label-text value
48  * out-of-range problem; removed 'draw()' method; added
49  * 'autoRangeMinimumSize' check; added 'log10TickLabelsFlag'
50  * parameter flag and implementation (ET);
51  * 25-Jun-2002 : Removed redundant import (DG);
52  * 25-Jul-2002 : Changed order of parameters in ValueAxis constructor (DG);
53  * 16-Jul-2002 : Implemented support for plotting positive values arbitrarily
54  * close to zero (added 'allowNegativesFlag' flag) (ET).
55  * 05-Sep-2002 : Updated constructor reflecting changes in the Axis class (DG);
56  * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
57  * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
58  * 22-Nov-2002 : Bug fixes from David M. O'Donnell (DG);
59  * 14-Jan-2003 : Changed autoRangeMinimumSize from Number --> double (DG);
60  * 20-Jan-2003 : Removed unnecessary constructors (DG);
61  * 26-Mar-2003 : Implemented Serializable (DG);
62  * 08-May-2003 : Fixed plotting of datasets with lower==upper bounds when
63  * 'minAutoRange' is very small; added 'strictValuesFlag'
64  * and default functionality of throwing a runtime exception
65  * if 'allowNegativesFlag' is false and any values are less
66  * than or equal to zero; added 'expTickLabelsFlag' and
67  * changed to use "1e#"-style tick labels by default
68  * ("10^n"-style tick labels still supported via 'set'
69  * method); improved generation of tick labels when range of
70  * values is small; changed to use 'NumberFormat.getInstance()'
71  * to create 'numberFormatterObj' (ET);
72  * 14-May-2003 : Merged HorizontalLogarithmicAxis and
73  * VerticalLogarithmicAxis (DG);
74  * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
75  * 07-Nov-2003 : Modified to use new NumberTick class (DG);
76  * 08-Apr-2004 : Use numberFormatOverride if set - see patch 930139 (DG);
77  * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
78  * 21-Apr-2005 : Added support for upper and lower margins; added
79  * get/setAutoRangeNextLogFlag() methods and changed
80  * default to 'autoRangeNextLogFlag'==false (ET);
81  * 22-Apr-2005 : Removed refreshTicks() and fixed names and parameters for
82  * refreshHorizontalTicks() & refreshVerticalTicks();
83  * changed javadoc on setExpTickLabelsFlag() to specify
84  * proper default (ET);
85  * 22-Apr-2005 : Renamed refreshHorizontalTicks --> refreshTicksHorizontal
86  * (and likewise the vertical version) for consistency with
87  * other axis classes (DG);
88  *
89  */

90
91 package org.jfree.chart.axis;
92
93 import java.awt.Graphics2D JavaDoc;
94 import java.awt.geom.Rectangle2D JavaDoc;
95 import java.text.DecimalFormat JavaDoc;
96 import java.text.NumberFormat JavaDoc;
97 import java.util.List JavaDoc;
98
99 import org.jfree.chart.plot.Plot;
100 import org.jfree.chart.plot.ValueAxisPlot;
101 import org.jfree.data.Range;
102 import org.jfree.ui.RectangleEdge;
103 import org.jfree.ui.TextAnchor;
104
105 /**
106  * A numerical axis that uses a logarithmic scale.
107  *
108  * @author Michael Duffy
109  */

110 public class LogarithmicAxis extends NumberAxis {
111
112     /** For serialization. */
113     private static final long serialVersionUID = 2502918599004103054L;
114     
115     /** Useful constant for log(10). */
116     public static final double LOG10_VALUE = Math.log(10.0);
117
118     /** Smallest arbitrarily-close-to-zero value allowed. */
119     public static final double SMALL_LOG_VALUE = 1e-100;
120
121     /** Flag set true to allow negative values in data. */
122     protected boolean allowNegativesFlag = false;
123
124     /** Flag set true make axis throw exception if any values are
125       * <= 0 and 'allowNegativesFlag' is false. */

126     protected boolean strictValuesFlag = true;
127
128     /** Number formatter for generating numeric strings. */
129     protected final NumberFormat JavaDoc numberFormatterObj
130         = NumberFormat.getInstance();
131
132     /** Flag set true for "1e#"-style tick labels. */
133     protected boolean expTickLabelsFlag = false;
134
135     /** Flag set true for "10^n"-style tick labels. */
136     protected boolean log10TickLabelsFlag = false;
137
138     /** True to make 'autoAdjustRange()' select "10^n" values. */
139     protected boolean autoRangeNextLogFlag = false;
140
141     /** Helper flag for log axis processing. */
142     protected boolean smallLogFlag = false;
143
144     /**
145      * Creates a new axis.
146      *
147      * @param label the axis label.
148      */

149     public LogarithmicAxis(String JavaDoc label) {
150         super(label);
151         setupNumberFmtObj(); //setup number formatter obj
152
}
153
154     /**
155      * Sets the 'allowNegativesFlag' flag; true to allow negative values
156      * in data, false to be able to plot positive values arbitrarily close to
157      * zero.
158      *
159      * @param flgVal the new value of the flag.
160      */

161     public void setAllowNegativesFlag(boolean flgVal) {
162         this.allowNegativesFlag = flgVal;
163     }
164
165     /**
166      * Returns the 'allowNegativesFlag' flag; true to allow negative values
167      * in data, false to be able to plot positive values arbitrarily close
168      * to zero.
169      *
170      * @return The flag.
171      */

172     public boolean getAllowNegativesFlag() {
173         return this.allowNegativesFlag;
174     }
175
176     /**
177      * Sets the 'strictValuesFlag' flag; if true and 'allowNegativesFlag'
178      * is false then this axis will throw a runtime exception if any of its
179      * values are less than or equal to zero; if false then the axis will
180      * adjust for values less than or equal to zero as needed.
181      *
182      * @param flgVal true for strict enforcement.
183      */

184     public void setStrictValuesFlag(boolean flgVal) {
185         this.strictValuesFlag = flgVal;
186     }
187
188     /**
189      * Returns the 'strictValuesFlag' flag; if true and 'allowNegativesFlag'
190      * is false then this axis will throw a runtime exception if any of its
191      * values are less than or equal to zero; if false then the axis will
192      * adjust for values less than or equal to zero as needed.
193      *
194      * @return <code>true</code> if strict enforcement is enabled.
195      */

196     public boolean getStrictValuesFlag() {
197         return this.strictValuesFlag;
198     }
199
200     /**
201      * Sets the 'expTickLabelsFlag' flag. If the 'log10TickLabelsFlag'
202      * is false then this will set whether or not "1e#"-style tick labels
203      * are used. The default is to use regular numeric tick labels.
204      *
205      * @param flgVal true for "1e#"-style tick labels, false for
206      * log10 or regular numeric tick labels.
207      */

208     public void setExpTickLabelsFlag(boolean flgVal) {
209         this.expTickLabelsFlag = flgVal;
210         setupNumberFmtObj(); //setup number formatter obj
211
}
212
213     /**
214      * Returns the 'expTickLabelsFlag' flag.
215      *
216      * @return <code>true</code> for "1e#"-style tick labels,
217      * <code>false</code> for log10 or regular numeric tick labels.
218      */

219     public boolean getExpTickLabelsFlag() {
220       return this.expTickLabelsFlag;
221     }
222
223     /**
224      * Sets the 'log10TickLabelsFlag' flag. The default value is false.
225      *
226      * @param flag true for "10^n"-style tick labels, false for "1e#"-style
227      * or regular numeric tick labels.
228      */

229     public void setLog10TickLabelsFlag(boolean flag) {
230         this.log10TickLabelsFlag = flag;
231     }
232
233     /**
234      * Returns the 'log10TickLabelsFlag' flag.
235      *
236      * @return <code>true</code> for "10^n"-style tick labels,
237      * <code>false</code> for "1e#"-style or regular numeric tick
238      * labels.
239      */

240     public boolean getLog10TickLabelsFlag() {
241         return this.log10TickLabelsFlag;
242     }
243
244     /**
245      * Sets the 'autoRangeNextLogFlag' flag. This determines whether or
246      * not the 'autoAdjustRange()' method will select the next "10^n"
247      * values when determining the upper and lower bounds. The default
248      * value is false.
249      *
250      * @param flag <code>true</code> to make the 'autoAdjustRange()'
251      * method select the next "10^n" values, <code>false</code> to not.
252      */

253     public void setAutoRangeNextLogFlag(boolean flag) {
254         this.autoRangeNextLogFlag = flag;
255     }
256
257     /**
258      * Returns the 'autoRangeNextLogFlag' flag.
259      *
260      * @return <code>true</code> if the 'autoAdjustRange()' method will
261      * select the next "10^n" values, <code>false</code> if not.
262      */

263     public boolean getAutoRangeNextLogFlag() {
264         return this.autoRangeNextLogFlag;
265     }
266
267     /**
268      * Overridden version that calls original and then sets up flag for
269      * log axis processing.
270      *
271      * @param range the new range.
272      */

273     public void setRange(Range range) {
274         super.setRange(range); // call parent method
275
setupSmallLogFlag(); // setup flag based on bounds values
276
}
277
278     /**
279      * Sets up flag for log axis processing. Set true if negative values
280      * not allowed and the lower bound is between 0 and 10.
281      */

282     protected void setupSmallLogFlag() {
283         // set flag true if negative values not allowed and the
284
// lower bound is between 0 and 10:
285
double lowerVal = getRange().getLowerBound();
286         this.smallLogFlag
287             = (!this.allowNegativesFlag && lowerVal < 10.0 && lowerVal > 0.0);
288     }
289
290     /**
291      * Sets up the number formatter object according to the
292      * 'expTickLabelsFlag' flag.
293      */

294     protected void setupNumberFmtObj() {
295         if (this.numberFormatterObj instanceof DecimalFormat JavaDoc) {
296             //setup for "1e#"-style tick labels or regular
297
// numeric tick labels, depending on flag:
298
((DecimalFormat JavaDoc) this.numberFormatterObj).applyPattern(
299                 this.expTickLabelsFlag ? "0E0" : "0.###"
300             );
301         }
302     }
303
304     /**
305      * Returns the log10 value, depending on if values between 0 and
306      * 1 are being plotted. If negative values are not allowed and
307      * the lower bound is between 0 and 10 then a normal log is
308      * returned; otherwise the returned value is adjusted if the
309      * given value is less than 10.
310      *
311      * @param val the value.
312      *
313      * @return log<sub>10</sub>(val).
314      */

315     protected double switchedLog10(double val) {
316         return this.smallLogFlag ? Math.log(val)
317                 / LOG10_VALUE : adjustedLog10(val);
318     }
319
320     /**
321      * Returns an adjusted log10 value for graphing purposes. The first
322      * adjustment is that negative values are changed to positive during
323      * the calculations, and then the answer is negated at the end. The
324      * second is that, for values less than 10, an increasingly large
325      * (0 to 1) scaling factor is added such that at 0 the value is
326      * adjusted to 1, resulting in a returned result of 0.
327      *
328      * @param val value for which log10 should be calculated.
329      *
330      * @return An adjusted log<sub>10</sub>(val).
331      */

332     public double adjustedLog10(double val) {
333         boolean negFlag = (val < 0.0);
334         if (negFlag) {
335             val = -val; // if negative then set flag and make positive
336
}
337         if (val < 10.0) { // if < 10 then
338
val += (10.0 - val) / 10; //increase so 0 translates to 0
339
}
340         //return value; negate if original value was negative:
341
return negFlag ? -(Math.log(val) / LOG10_VALUE)
342                 : (Math.log(val) / LOG10_VALUE);
343     }
344
345     /**
346      * Returns the largest (closest to positive infinity) double value that is
347      * not greater than the argument, is equal to a mathematical integer and
348      * satisfying the condition that log base 10 of the value is an integer
349      * (i.e., the value returned will be a power of 10: 1, 10, 100, 1000, etc.).
350      *
351      * @param lower a double value below which a floor will be calcualted.
352      *
353      * @return 10<sup>N</sup> with N .. { 1 ... }
354      */

355     protected double computeLogFloor(double lower) {
356
357         double logFloor;
358         if (this.allowNegativesFlag) {
359             //negative values are allowed
360
if (lower > 10.0) { //parameter value is > 10
361
// The Math.log() function is based on e not 10.
362
logFloor = Math.log(lower) / LOG10_VALUE;
363                 logFloor = Math.floor(logFloor);
364                 logFloor = Math.pow(10, logFloor);
365             }
366             else if (lower < -10.0) { //parameter value is < -10
367
//calculate log using positive value:
368
logFloor = Math.log(-lower) / LOG10_VALUE;
369                 //calculate floor using negative value:
370
logFloor = Math.floor(-logFloor);
371                 //calculate power using positive value; then negate
372
logFloor = -Math.pow(10, -logFloor);
373             }
374             else {
375                 //parameter value is -10 > val < 10
376
logFloor = Math.floor(lower); //use as-is
377
}
378         }
379         else {
380             //negative values not allowed
381
if (lower > 0.0) { //parameter value is > 0
382
// The Math.log() function is based on e not 10.
383
logFloor = Math.log(lower) / LOG10_VALUE;
384                 logFloor = Math.floor(logFloor);
385                 logFloor = Math.pow(10, logFloor);
386             }
387             else {
388                 //parameter value is <= 0
389
logFloor = Math.floor(lower); //use as-is
390
}
391         }
392         return logFloor;
393     }
394
395     /**
396      * Returns the smallest (closest to negative infinity) double value that is
397      * not less than the argument, is equal to a mathematical integer and
398      * satisfying the condition that log base 10 of the value is an integer
399      * (i.e., the value returned will be a power of 10: 1, 10, 100, 1000, etc.).
400      *
401      * @param upper a double value above which a ceiling will be calcualted.
402      *
403      * @return 10<sup>N</sup> with N .. { 1 ... }
404      */

405     protected double computeLogCeil(double upper) {
406
407         double logCeil;
408         if (this.allowNegativesFlag) {
409             //negative values are allowed
410
if (upper > 10.0) {
411                 //parameter value is > 10
412
// The Math.log() function is based on e not 10.
413
logCeil = Math.log(upper) / LOG10_VALUE;
414                 logCeil = Math.ceil(logCeil);
415                 logCeil = Math.pow(10, logCeil);
416             }
417             else if (upper < -10.0) {
418                 //parameter value is < -10
419
//calculate log using positive value:
420
logCeil = Math.log(-upper) / LOG10_VALUE;
421                 //calculate ceil using negative value:
422
logCeil = Math.ceil(-logCeil);
423                 //calculate power using positive value; then negate
424
logCeil = -Math.pow(10, -logCeil);
425             }
426             else {
427                //parameter value is -10 > val < 10
428
logCeil = Math.ceil(upper); //use as-is
429
}
430         }
431         else {
432             //negative values not allowed
433
if (upper > 0.0) {
434                 //parameter value is > 0
435
// The Math.log() function is based on e not 10.
436
logCeil = Math.log(upper) / LOG10_VALUE;
437                 logCeil = Math.ceil(logCeil);
438                 logCeil = Math.pow(10, logCeil);
439             }
440             else {
441                 //parameter value is <= 0
442
logCeil = Math.ceil(upper); //use as-is
443
}
444         }
445         return logCeil;
446     }
447
448     /**
449      * Rescales the axis to ensure that all data is visible.
450      */

451     public void autoAdjustRange() {
452
453         Plot plot = getPlot();
454         if (plot == null) {
455             return; // no plot, no data.
456
}
457
458         if (plot instanceof ValueAxisPlot) {
459             ValueAxisPlot vap = (ValueAxisPlot) plot;
460
461             double lower;
462             Range r = vap.getDataRange(this);
463             if (r == null) {
464                    //no real data present
465
r = new Range(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND);
466                 lower = r.getLowerBound(); //get lower bound value
467
}
468             else {
469                 //actual data is present
470
lower = r.getLowerBound(); //get lower bound value
471
if (this.strictValuesFlag
472                         && !this.allowNegativesFlag && lower <= 0.0) {
473                     //strict flag set, allow-negatives not set and values <= 0
474
throw new RuntimeException JavaDoc(
475                         "Values less than or equal to "
476                         + "zero not allowed with logarithmic axis"
477                     );
478                 }
479             }
480
481             //apply lower margin by decreasing lower bound:
482
final double lowerMargin;
483             if (lower > 0.0 && (lowerMargin=getLowerMargin()) > 0.0) {
484                    //lower bound and margin OK; get log10 of lower bound
485
final double logLower = (Math.log(lower) / LOG10_VALUE);
486               double logAbs; //get absolute value of log10 value
487
if((logAbs=Math.abs(logLower)) < 1.0) {
488                 logAbs = 1.0; //if less than 1.0 then make it 1.0
489
} //subtract out margin and get exponential value:
490
lower = Math.pow(10, (logLower - (logAbs * lowerMargin)));
491             }
492
493             //if flag then change to log version of lowest value
494
// to make range begin at a 10^n value:
495
if (this.autoRangeNextLogFlag) {
496                lower = computeLogFloor(lower);
497            }
498
499             if (!this.allowNegativesFlag && lower >= 0.0
500                     && lower < SMALL_LOG_VALUE) {
501                 //negatives not allowed and lower range bound is zero
502
lower = r.getLowerBound(); //use data range bound instead
503
}
504
505             double upper = r.getUpperBound();
506
507              //apply upper margin by increasing upper bound:
508
final double upperMargin;
509             if (upper > 0.0 && (upperMargin=getUpperMargin()) > 0.0) {
510                    //upper bound and margin OK; get log10 of upper bound
511
final double logUpper = (Math.log(upper) / LOG10_VALUE);
512               double logAbs; //get absolute value of log10 value
513
if((logAbs=Math.abs(logUpper)) < 1.0) {
514                 logAbs = 1.0; //if less than 1.0 then make it 1.0
515
} //add in margin and get exponential value:
516
upper = Math.pow(10, (logUpper + (logAbs * upperMargin)));
517             }
518
519             if (!this.allowNegativesFlag && upper < 1.0 && upper > 0.0
520                     && lower > 0.0) {
521                 //negatives not allowed and upper bound between 0 & 1
522
//round up to nearest significant digit for bound:
523
//get negative exponent:
524
double expVal = Math.log(upper) / LOG10_VALUE;
525                 expVal = Math.ceil(-expVal + 0.001); //get positive exponent
526
expVal = Math.pow(10, expVal); //create multiplier value
527
//multiply, round up, and divide for bound value:
528
upper = (expVal > 0.0) ? Math.ceil(upper * expVal) / expVal
529                     : Math.ceil(upper);
530             }
531             else {
532                 //negatives allowed or upper bound not between 0 & 1
533
//if flag then change to log version of highest value to
534
// make range begin at a 10^n value; else use nearest int
535
upper = (this.autoRangeNextLogFlag) ? computeLogCeil(upper)
536                     : Math.ceil(upper);
537             }
538             // ensure the autorange is at least <minRange> in size...
539
double minRange = getAutoRangeMinimumSize();
540             if (upper - lower < minRange) {
541                 upper = (upper + lower + minRange) / 2;
542                 lower = (upper + lower - minRange) / 2;
543                 //if autorange still below minimum then adjust by 1%
544
// (can be needed when minRange is very small):
545
if (upper - lower < minRange) {
546                     double absUpper = Math.abs(upper);
547                     //need to account for case where upper==0.0
548
double adjVal = (absUpper > SMALL_LOG_VALUE) ? absUpper
549                         / 100.0 : 0.01;
550                     upper = (upper + lower + adjVal) / 2;
551                     lower = (upper + lower - adjVal) / 2;
552                 }
553             }
554
555             setRange(new Range(lower, upper), false, false);
556             setupSmallLogFlag(); //setup flag based on bounds values
557
}
558     }
559
560     /**
561      * Converts a data value to a coordinate in Java2D space, assuming that
562      * the axis runs along one edge of the specified plotArea.
563      * Note that it is possible for the coordinate to fall outside the
564      * plotArea.
565      *
566      * @param value the data value.
567      * @param plotArea the area for plotting the data.
568      * @param edge the axis location.
569      *
570      * @return The Java2D coordinate.
571      */

572     public double valueToJava2D(double value, Rectangle2D JavaDoc plotArea,
573                                 RectangleEdge edge) {
574
575         Range range = getRange();
576         double axisMin = switchedLog10(range.getLowerBound());
577         double axisMax = switchedLog10(range.getUpperBound());
578
579         double min = 0.0;
580         double max = 0.0;
581         if (RectangleEdge.isTopOrBottom(edge)) {
582             min = plotArea.getMinX();
583             max = plotArea.getMaxX();
584         }
585         else if (RectangleEdge.isLeftOrRight(edge)) {
586             min = plotArea.getMaxY();
587             max = plotArea.getMinY();
588         }
589
590         value = switchedLog10(value);
591
592         if (isInverted()) {
593             return max
594                 - (((value - axisMin) / (axisMax - axisMin)) * (max - min));
595         }
596         else {
597             return min
598                 + (((value - axisMin) / (axisMax - axisMin)) * (max - min));
599         }
600
601     }
602
603     /**
604      * Converts a coordinate in Java2D space to the corresponding data
605      * value, assuming that the axis runs along one edge of the specified
606      * plotArea.
607      *
608      * @param java2DValue the coordinate in Java2D space.
609      * @param plotArea the area in which the data is plotted.
610      * @param edge the axis location.
611      *
612      * @return The data value.
613      */

614     public double java2DToValue(double java2DValue, Rectangle2D JavaDoc plotArea,
615                                 RectangleEdge edge) {
616
617         Range range = getRange();
618         double axisMin = switchedLog10(range.getLowerBound());
619         double axisMax = switchedLog10(range.getUpperBound());
620
621         double plotMin = 0.0;
622         double plotMax = 0.0;
623         if (RectangleEdge.isTopOrBottom(edge)) {
624             plotMin = plotArea.getX();
625             plotMax = plotArea.getMaxX();
626         }
627         else if (RectangleEdge.isLeftOrRight(edge)) {
628             plotMin = plotArea.getMaxY();
629             plotMax = plotArea.getMinY();
630         }
631
632         if (isInverted()) {
633             return Math.pow(
634                 10, axisMax - ((java2DValue - plotMin) / (plotMax - plotMin))
635                 * (axisMax - axisMin)
636             );
637         }
638         else {
639             return Math.pow(
640                 10, axisMin + ((java2DValue - plotMin) / (plotMax - plotMin))
641                 * (axisMax - axisMin)
642             );
643         }
644     }
645
646     /**
647      * Calculates the positions of the tick labels for the axis, storing the
648      * results in the tick label list (ready for drawing).
649      *
650      * @param g2 the graphics device.
651      * @param dataArea the area in which the plot should be drawn.
652      * @param edge the location of the axis.
653      *
654      * @return A list of ticks.
655      */

656     protected List JavaDoc refreshTicksHorizontal(Graphics2D JavaDoc g2,
657                                           Rectangle2D JavaDoc dataArea,
658                                           RectangleEdge edge) {
659
660         List JavaDoc ticks = new java.util.ArrayList JavaDoc();
661         Range range = getRange();
662
663         //get lower bound value:
664
double lowerBoundVal = range.getLowerBound();
665               //if small log values and lower bound value too small
666
// then set to a small value (don't allow <= 0):
667
if (this.smallLogFlag && lowerBoundVal < SMALL_LOG_VALUE) {
668             lowerBoundVal = SMALL_LOG_VALUE;
669         }
670
671         //get upper bound value
672
double upperBoundVal = range.getUpperBound();
673
674         //get log10 version of lower bound and round to integer:
675
int iBegCount = (int) Math.rint(switchedLog10(lowerBoundVal));
676         //get log10 version of upper bound and round to integer:
677
int iEndCount = (int) Math.rint(switchedLog10(upperBoundVal));
678
679         if (iBegCount == iEndCount && iBegCount > 0
680                 && Math.pow(10, iBegCount) > lowerBoundVal) {
681               //only 1 power of 10 value, it's > 0 and its resulting
682
// tick value will be larger than lower bound of data
683
--iBegCount; //decrement to generate more ticks
684
}
685
686         double currentTickValue;
687         String JavaDoc tickLabel;
688         boolean zeroTickFlag = false;
689         for (int i = iBegCount; i <= iEndCount; i++) {
690             //for each power of 10 value; create ten ticks
691
for (int j = 0; j < 10; ++j) {
692                 //for each tick to be displayed
693
if (this.smallLogFlag) {
694                     //small log values in use; create numeric value for tick
695
currentTickValue = Math.pow(10, i) + (Math.pow(10, i) * j);
696                     if (this.expTickLabelsFlag
697                         || (i < 0 && currentTickValue > 0.0
698                         && currentTickValue < 1.0)) {
699                         //showing "1e#"-style ticks or negative exponent
700
// generating tick value between 0 & 1; show fewer
701
if (j == 0 || (i > -4 && j < 2)
702                                    || currentTickValue >= upperBoundVal) {
703                           //first tick of series, or not too small a value and
704
// one of first 3 ticks, or last tick to be displayed
705
// set exact number of fractional digits to be shown
706
// (no effect if showing "1e#"-style ticks):
707
this.numberFormatterObj
708                                 .setMaximumFractionDigits(-i);
709                                //create tick label (force use of fmt obj):
710
tickLabel = makeTickLabel(currentTickValue, true);
711                         }
712                         else { //no tick label to be shown
713
tickLabel = "";
714                         }
715                     }
716                     else { //tick value not between 0 & 1
717
//show tick label if it's the first or last in
718
// the set, or if it's 1-5; beyond that show
719
// fewer as the values get larger:
720
tickLabel = (j < 1 || (i < 1 && j < 5) || (j < 4 - i)
721                                          || currentTickValue >= upperBoundVal)
722                                          ? makeTickLabel(currentTickValue) : "";
723                     }
724                 }
725                 else { //not small log values in use; allow for values <= 0
726
if (zeroTickFlag) { //if did zero tick last iter then
727
--j; //decrement to do 1.0 tick now
728
} //calculate power-of-ten value for tick:
729
currentTickValue = (i >= 0)
730                         ? Math.pow(10, i) + (Math.pow(10, i) * j)
731                         : -(Math.pow(10, -i) - (Math.pow(10, -i - 1) * j));
732                     if (!zeroTickFlag) { // did not do zero tick last iteration
733
if (Math.abs(currentTickValue - 1.0) < 0.0001
734                             && lowerBoundVal <= 0.0 && upperBoundVal >= 0.0) {
735                             //tick value is 1.0 and 0.0 is within data range
736
currentTickValue = 0.0; //set tick value to zero
737
zeroTickFlag = true; //indicate zero tick
738
}
739                     }
740                     else { //did zero tick last iteration
741
zeroTickFlag = false; //clear flag
742
} //create tick label string:
743
//show tick label if "1e#"-style and it's one
744
// of the first two, if it's the first or last
745
// in the set, or if it's 1-5; beyond that
746
// show fewer as the values get larger:
747
tickLabel = ((this.expTickLabelsFlag && j < 2)
748                                 || j < 1
749                                 || (i < 1 && j < 5) || (j < 4 - i)
750                                 || currentTickValue >= upperBoundVal)
751                                    ? makeTickLabel(currentTickValue) : "";
752                 }
753
754                 if (currentTickValue > upperBoundVal) {
755                     return ticks; // if past highest data value then exit
756
// method
757
}
758
759                 if (currentTickValue >= lowerBoundVal - SMALL_LOG_VALUE) {
760                     //tick value not below lowest data value
761
TextAnchor anchor = null;
762                     TextAnchor rotationAnchor = null;
763                     double angle = 0.0;
764                     if (isVerticalTickLabels()) {
765                         anchor = TextAnchor.CENTER_RIGHT;
766                         rotationAnchor = TextAnchor.CENTER_RIGHT;
767                         if (edge == RectangleEdge.TOP) {
768                             angle = Math.PI / 2.0;
769                         }
770                         else {
771                             angle = -Math.PI / 2.0;
772                         }
773                     }
774                     else {
775                         if (edge == RectangleEdge.TOP) {
776                             anchor = TextAnchor.BOTTOM_CENTER;
777                             rotationAnchor = TextAnchor.BOTTOM_CENTER;
778                         }
779                         else {
780                             anchor = TextAnchor.TOP_CENTER;
781                             rotationAnchor = TextAnchor.TOP_CENTER;
782                         }
783                     }
784
785                     Tick tick = new NumberTick(
786                         new Double JavaDoc(currentTickValue), tickLabel, anchor,
787                         rotationAnchor, angle
788                     );
789                     ticks.add(tick);
790                 }
791             }
792         }
793         return ticks;
794
795     }
796
797     /**
798      * Calculates the positions of the tick labels for the axis, storing the
799      * results in the tick label list (ready for drawing).
800      *
801      * @param g2 the graphics device.
802      * @param dataArea the area in which the plot should be drawn.
803      * @param edge the location of the axis.
804      *
805      * @return A list of ticks.
806      */

807     protected List JavaDoc refreshTicksVertical(Graphics2D JavaDoc g2,
808                                         Rectangle2D JavaDoc dataArea,
809                                         RectangleEdge edge) {
810
811         List JavaDoc ticks = new java.util.ArrayList JavaDoc();
812
813         //get lower bound value:
814
double lowerBoundVal = getRange().getLowerBound();
815         //if small log values and lower bound value too small
816
// then set to a small value (don't allow <= 0):
817
if (this.smallLogFlag && lowerBoundVal < SMALL_LOG_VALUE) {
818             lowerBoundVal = SMALL_LOG_VALUE;
819         }
820         //get upper bound value
821
double upperBoundVal = getRange().getUpperBound();
822
823         //get log10 version of lower bound and round to integer:
824
int iBegCount = (int) Math.rint(switchedLog10(lowerBoundVal));
825         //get log10 version of upper bound and round to integer:
826
int iEndCount = (int) Math.rint(switchedLog10(upperBoundVal));
827
828         if (iBegCount == iEndCount && iBegCount > 0
829                 && Math.pow(10, iBegCount) > lowerBoundVal) {
830               //only 1 power of 10 value, it's > 0 and its resulting
831
// tick value will be larger than lower bound of data
832
--iBegCount; //decrement to generate more ticks
833
}
834
835         double tickVal;
836         String JavaDoc tickLabel;
837         boolean zeroTickFlag = false;
838         for (int i = iBegCount; i <= iEndCount; i++) {
839             //for each tick with a label to be displayed
840
int jEndCount = 10;
841             if (i == iEndCount) {
842                 jEndCount = 1;
843             }
844
845             for (int j = 0; j < jEndCount; j++) {
846                 //for each tick to be displayed
847
if (this.smallLogFlag) {
848                     //small log values in use
849
tickVal = Math.pow(10, i) + (Math.pow(10, i) * j);
850                     if (j == 0) {
851                         //first tick of group; create label text
852
if (this.log10TickLabelsFlag) {
853                             //if flag then
854
tickLabel = "10^" + i; //create "log10"-type label
855
}
856                         else { //not "log10"-type label
857
if (this.expTickLabelsFlag) {
858                                 //if flag then
859
tickLabel = "1e" + i; //create "1e#"-type label
860
}
861                             else { //not "1e#"-type label
862
if (i >= 0) { // if positive exponent then
863
// make integer
864
NumberFormat JavaDoc format
865                                         = getNumberFormatOverride();
866                                     if (format != null) {
867                                         tickLabel = format.format(tickVal);
868                                     }
869                                     else {
870                                         tickLabel = Long.toString((long)
871                                                 Math.rint(tickVal));
872                                     }
873                                 }
874                                 else {
875                                     //negative exponent; create fractional value
876
//set exact number of fractional digits to
877
// be shown:
878
this.numberFormatterObj
879                                         .setMaximumFractionDigits(-i);
880                                     //create tick label:
881
tickLabel = this.numberFormatterObj.format(
882                                         tickVal
883                                     );
884                                 }
885                             }
886                         }
887                     }
888                     else { //not first tick to be displayed
889
tickLabel = ""; //no tick label
890
}
891                 }
892                 else { //not small log values in use; allow for values <= 0
893
if (zeroTickFlag) { //if did zero tick last iter then
894
--j;
895                     } //decrement to do 1.0 tick now
896
tickVal = (i >= 0) ? Math.pow(10, i) + (Math.pow(10, i) * j)
897                              : -(Math.pow(10, -i) - (Math.pow(10, -i - 1) * j));
898                     if (j == 0) { //first tick of group
899
if (!zeroTickFlag) { // did not do zero tick last
900
// iteration
901
if (i > iBegCount && i < iEndCount
902                                     && Math.abs(tickVal - 1.0) < 0.0001) {
903                                 // not first or last tick on graph and value
904
// is 1.0
905
tickVal = 0.0; //change value to 0.0
906
zeroTickFlag = true; //indicate zero tick
907
tickLabel = "0"; //create label for tick
908
}
909                             else {
910                                 //first or last tick on graph or value is 1.0
911
//create label for tick:
912
if (this.log10TickLabelsFlag) {
913                                        //create "log10"-type label
914
tickLabel = (((i < 0) ? "-" : "")
915                                             + "10^" + Math.abs(i));
916                                 }
917                                 else {
918                                     if (this.expTickLabelsFlag) {
919                                            //create "1e#"-type label
920
tickLabel = (((i < 0) ? "-" : "")
921                                                 + "1e" + Math.abs(i));
922                                     }
923                                     else {
924                                         NumberFormat JavaDoc format
925                                             = getNumberFormatOverride();
926                                         if (format != null) {
927                                             tickLabel = format.format(tickVal);
928                                         }
929                                         else {
930                                             tickLabel = Long.toString(
931                                                 (long) Math.rint(tickVal)
932                                             );
933                                         }
934                                     }
935                                 }
936                             }
937                         }
938                         else { // did zero tick last iteration
939
tickLabel = ""; //no label
940
zeroTickFlag = false; //clear flag
941
}
942                     }
943                     else { // not first tick of group
944
tickLabel = ""; //no label
945
zeroTickFlag = false; //make sure flag cleared
946
}
947                 }
948
949                 if (tickVal > upperBoundVal) {
950                     return ticks; //if past highest data value then exit method
951
}
952
953                 if (tickVal >= lowerBoundVal - SMALL_LOG_VALUE) {
954                     //tick value not below lowest data value
955
TextAnchor anchor = null;
956                     TextAnchor rotationAnchor = null;
957                     double angle = 0.0;
958                     if (isVerticalTickLabels()) {
959                         if (edge == RectangleEdge.LEFT) {
960                             anchor = TextAnchor.BOTTOM_CENTER;
961                             rotationAnchor = TextAnchor.BOTTOM_CENTER;
962                             angle = -Math.PI / 2.0;
963                         }
964                         else {
965                             anchor = TextAnchor.BOTTOM_CENTER;
966                             rotationAnchor = TextAnchor.BOTTOM_CENTER;
967                             angle = Math.PI / 2.0;
968                         }
969                     }
970                     else {
971                         if (edge == RectangleEdge.LEFT) {
972                             anchor = TextAnchor.CENTER_RIGHT;
973                             rotationAnchor = TextAnchor.CENTER_RIGHT;
974                         }
975                         else {
976                             anchor = TextAnchor.CENTER_LEFT;
977                             rotationAnchor = TextAnchor.CENTER_LEFT;
978                         }
979                     }
980                     //create tick object and add to list:
981
ticks.add(
982                         new NumberTick(
983                             new Double JavaDoc(tickVal), tickLabel, anchor,
984                             rotationAnchor, angle
985                         )
986                     );
987
988                 }
989             }
990         }
991         return ticks;
992     }
993
994     /**
995      * Converts the given value to a tick label string.
996      *
997      * @param val the value to convert.
998      * @param forceFmtFlag true to force the number-formatter object
999      * to be used.
1000     *
1001     * @return The tick label string.
1002     */

1003    protected String JavaDoc makeTickLabel(double val, boolean forceFmtFlag) {
1004        if (this.expTickLabelsFlag || forceFmtFlag) {
1005            //using exponents or force-formatter flag is set
1006
// (convert 'E' to lower-case 'e'):
1007
return this.numberFormatterObj.format(val).toLowerCase();
1008        }
1009        return getTickUnit().valueToString(val);
1010    }
1011
1012    /**
1013     * Converts the given value to a tick label string.
1014     * @param val the value to convert.
1015     *
1016     * @return The tick label string.
1017     */

1018    protected String JavaDoc makeTickLabel(double val) {
1019        return makeTickLabel(val, false);
1020    }
1021
1022}
1023
Popular Tags