001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it
010     * under the terms of the GNU Lesser General Public License as published by
011     * the Free Software Foundation; either version 2.1 of the License, or
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025     * in the United States and other countries.]
026     *
027     * --------------------
028     * LogarithmicAxis.java
029     * --------------------
030     * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
031     *
032     * Original Author:  Michael Duffy / Eric Thomas;
033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
034     *                   David M. O'Donnell;
035     *                   Scott Sams;
036     *
037     * $Id: LogarithmicAxis.java,v 1.11.2.3 2007/02/02 14:32:42 mungady Exp $
038     *
039     * Changes
040     * -------
041     * 14-Mar-2002 : Version 1 contributed by Michael Duffy (DG);
042     * 19-Apr-2002 : drawVerticalString() is now drawRotatedString() in
043     *               RefineryUtilities (DG);
044     * 23-Apr-2002 : Added a range property (DG);
045     * 15-May-2002 : Modified to be able to deal with negative and zero values (via
046     *               new 'adjustedLog10()' method);  occurrences of "Math.log(10)"
047     *               changed to "LOG10_VALUE"; changed 'intValue()' to
048     *               'longValue()' in 'refreshTicks()' to fix label-text value
049     *               out-of-range problem; removed 'draw()' method; added
050     *               'autoRangeMinimumSize' check; added 'log10TickLabelsFlag'
051     *               parameter flag and implementation (ET);
052     * 25-Jun-2002 : Removed redundant import (DG);
053     * 25-Jul-2002 : Changed order of parameters in ValueAxis constructor (DG);
054     * 16-Jul-2002 : Implemented support for plotting positive values arbitrarily
055     *               close to zero (added 'allowNegativesFlag' flag) (ET).
056     * 05-Sep-2002 : Updated constructor reflecting changes in the Axis class (DG);
057     * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
058     * 08-Nov-2002 : Moved to new package com.jrefinery.chart.axis (DG);
059     * 22-Nov-2002 : Bug fixes from David M. O'Donnell (DG);
060     * 14-Jan-2003 : Changed autoRangeMinimumSize from Number --> double (DG);
061     * 20-Jan-2003 : Removed unnecessary constructors (DG);
062     * 26-Mar-2003 : Implemented Serializable (DG);
063     * 08-May-2003 : Fixed plotting of datasets with lower==upper bounds when
064     *               'minAutoRange' is very small; added 'strictValuesFlag'
065     *               and default functionality of throwing a runtime exception
066     *               if 'allowNegativesFlag' is false and any values are less
067     *               than or equal to zero; added 'expTickLabelsFlag' and
068     *               changed to use "1e#"-style tick labels by default
069     *               ("10^n"-style tick labels still supported via 'set'
070     *               method); improved generation of tick labels when range of
071     *               values is small; changed to use 'NumberFormat.getInstance()'
072     *               to create 'numberFormatterObj' (ET);
073     * 14-May-2003 : Merged HorizontalLogarithmicAxis and
074     *               VerticalLogarithmicAxis (DG);
075     * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
076     * 07-Nov-2003 : Modified to use new NumberTick class (DG);
077     * 08-Apr-2004 : Use numberFormatOverride if set - see patch 930139 (DG);
078     * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
079     * 21-Apr-2005 : Added support for upper and lower margins; added
080     *               get/setAutoRangeNextLogFlag() methods and changed
081     *               default to 'autoRangeNextLogFlag'==false (ET);
082     * 22-Apr-2005 : Removed refreshTicks() and fixed names and parameters for
083     *               refreshHorizontalTicks() & refreshVerticalTicks();
084     *               changed javadoc on setExpTickLabelsFlag() to specify
085     *               proper default (ET);
086     * 22-Apr-2005 : Renamed refreshHorizontalTicks --> refreshTicksHorizontal
087     *               (and likewise the vertical version) for consistency with
088     *               other axis classes (DG);
089     * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
090     *
091     */
092    
093    package org.jfree.chart.axis;
094    
095    import java.awt.Graphics2D;
096    import java.awt.geom.Rectangle2D;
097    import java.text.DecimalFormat;
098    import java.text.NumberFormat;
099    import java.util.List;
100    
101    import org.jfree.chart.plot.Plot;
102    import org.jfree.chart.plot.ValueAxisPlot;
103    import org.jfree.data.Range;
104    import org.jfree.ui.RectangleEdge;
105    import org.jfree.ui.TextAnchor;
106    
107    /**
108     * A numerical axis that uses a logarithmic scale.
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 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 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) {
296                //setup for "1e#"-style tick labels or regular
297                // numeric tick labels, depending on flag:
298                ((DecimalFormat) 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(
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 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 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 refreshTicksHorizontal(Graphics2D g2,
657                                              Rectangle2D dataArea,
658                                              RectangleEdge edge) {
659    
660            List ticks = new java.util.ArrayList();
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 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(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 refreshTicksVertical(Graphics2D g2, 
808                                            Rectangle2D dataArea,
809                                            RectangleEdge edge) {
810    
811            List ticks = new java.util.ArrayList();
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 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 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 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(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 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 makeTickLabel(double val) {
1019            return makeTickLabel(val, false);
1020        }
1021    
1022    }