Bug 13403 fixed: Regression on axes labelling
[scilab.git] / scilab / modules / scirenderer / src / org / scilab / forge / scirenderer / ruler / graduations / AbstractGraduations.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando
4  * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11  */
12
13 package org.scilab.forge.scirenderer.ruler.graduations;
14
15 import java.util.Collections;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.text.DecimalFormat;
19 import java.text.DecimalFormatSymbols;
20
21 /**
22  * @author Pierre Lando
23  */
24 public abstract class AbstractGraduations implements Graduations {
25
26     protected static final double PRECISION = 1e-8;
27
28     /** The left bracket used by {@link #toString()} */
29     private static final String LEFT_BRACKET = "[";
30
31     /** The right bracket used by {@link #toString()} */
32     private static final String RIGHT_BRACKET = "]";
33
34     /** True if the lower bound is included in the graduation interval. */
35     private final boolean isLowerBoundIncluded;
36
37     /** True if the upper bound is included in the graduation interval. */
38     private final boolean isUpperBoundIncluded;
39
40     /** Interval lower bound. */
41     private final double lowerBound;
42
43     /** Interval upper bound. */
44     private final double upperBound;
45
46     private final Graduations parentGraduations;
47     private DecimalFormat numberFormat;
48     private List<Double> subValues;
49
50     /**
51      * Constructor from parent graduations.
52      * This constructor copy information from given {@link Graduations} and set it as is parent.
53      * @param parentGraduations the parent graduations to copy.
54      */
55     public AbstractGraduations(Graduations parentGraduations) {
56         this.parentGraduations = parentGraduations;
57         this.isLowerBoundIncluded = parentGraduations.isLowerBoundIncluded();
58         this.isUpperBoundIncluded = parentGraduations.isUpperBoundIncluded();
59         this.lowerBound = parentGraduations.getLowerBound();
60         this.upperBound = parentGraduations.getUpperBound();
61     }
62
63     /**
64      * Root constructor.
65      * Graduations made this way don't have a parent.
66      * @param lowerBound the actual lower bounds.
67      * @param lowerBoundIncluded the actual lower bounds included status.
68      * @param upperBound the actual upper bounds.
69      * @param upperBoundIncluded the actual upper bounds included status.
70      */
71     public AbstractGraduations(double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) {
72         this.parentGraduations = null;
73         this.isLowerBoundIncluded = lowerBoundIncluded;
74         this.isUpperBoundIncluded = upperBoundIncluded;
75         this.lowerBound = lowerBound;
76         this.upperBound = upperBound;
77     }
78
79     /**
80      * Root constructor.
81      * Graduations made this way don't have a parent.
82      * There bounds are included.
83      * @param lowerBound the actual lower bounds included status.
84      * @param upperBound the actual upper bounds included status.
85      */
86     public AbstractGraduations(double lowerBound, double upperBound) {
87         this.parentGraduations = null;
88         this.isLowerBoundIncluded = true;
89         this.isUpperBoundIncluded = true;
90         this.lowerBound = lowerBound;
91         this.upperBound = upperBound;
92     }
93
94     /**
95      * Child constructor.
96      * @param parentGraduations the parent graduation.
97      * @param lowerBound the actual lower bounds.
98      * @param lowerBoundIncluded the actual lower bounds included status.
99      * @param upperBound the actual upper bounds.
100      * @param upperBoundIncluded the actual upper bounds included status.
101      */
102     public AbstractGraduations(Graduations parentGraduations, double lowerBound, boolean lowerBoundIncluded, double upperBound, boolean upperBoundIncluded) {
103         this.parentGraduations = parentGraduations;
104         this.isLowerBoundIncluded = lowerBoundIncluded;
105         this.isUpperBoundIncluded = upperBoundIncluded;
106         this.lowerBound = lowerBound;
107         this.upperBound = upperBound;
108     }
109
110     @Override
111     public final double getLowerBound() {
112         return lowerBound;
113     }
114
115     @Override
116     public final boolean isLowerBoundIncluded() {
117         return isLowerBoundIncluded;
118     }
119
120     @Override
121     public final double getUpperBound() {
122         return upperBound;
123     }
124
125     @Override
126     public final boolean isUpperBoundIncluded() {
127         return isUpperBoundIncluded;
128     }
129
130     @Override
131     public final Graduations getParentGraduations() {
132         return parentGraduations;
133     }
134
135     @Override
136     public final boolean contain(double value) {
137         if (value == lowerBound) {
138             return isLowerBoundIncluded;
139         }
140         if (value == upperBound) {
141             return isUpperBoundIncluded;
142         }
143         return (lowerBound < value) && (value < upperBound);
144     }
145
146     /**
147      * Equivalent to contain but for interval [0, upper-lower] (to avoid rounding error in computations)
148      */
149     public final boolean containRelative(double value) {
150         if (value == 0 || Math.abs(value / (upperBound - lowerBound)) <= PRECISION) {
151             return isLowerBoundIncluded;
152         }
153         if (value == upperBound - lowerBound || Math.abs(1 - value / (upperBound - lowerBound)) <= PRECISION) {
154             return isUpperBoundIncluded;
155         }
156         return (0 < value) && (value < upperBound - lowerBound);
157     }
158
159     @Override
160     public final DecimalFormat getFormat() {
161         if (numberFormat == null) {
162             double maxDisplayedValue = Math.max(Math.abs(lowerBound), Math.abs(upperBound));
163             double len = Math.abs(upperBound - lowerBound);
164
165             if (maxDisplayedValue < 1e-3) {
166                 numberFormat = new DecimalFormat("0.##########E00");
167             } else if (false && len <= 1e-3) {
168                 // desactivated for now...
169                 // the user should be able to do that itself
170                 numberFormat = new TinyIntervalFormat("0.####E00", "0.##E00");
171             } else if (maxDisplayedValue >= 1e6) {
172                 numberFormat = new DecimalFormat("0.##########E00");
173             } else if (maxDisplayedValue < 1) {
174                 numberFormat = new DecimalFormat("0.######");
175             } else {
176                 numberFormat = new DecimalFormat("#,##0.####");
177             }
178
179             DecimalFormatSymbols decimalFormatSymbols = numberFormat.getDecimalFormatSymbols();
180             decimalFormatSymbols.setExponentSeparator("e");
181             numberFormat.setDecimalFormatSymbols(decimalFormatSymbols);
182         }
183         return numberFormat;
184     }
185
186     @Override
187     public List<Double> getSubGraduations(final int N) {
188         if (subValues == null) {
189             List<Double> ticksValue = getAllValues();
190             if (N == 0 || ticksValue.size() == 0) {
191                 subValues = new LinkedList<Double>();
192             } else {
193                 Collections.sort(ticksValue);
194                 subValues = new LinkedList<Double>();
195
196                 for (int i = 0; i < ticksValue.size() - 1; i++) {
197                     final double first = ticksValue.get(i);
198                     final double second = ticksValue.get(i + 1);
199                     final double step = (second - first) / (N + 1);
200                     double v = first;
201                     for (int j = 0; j <= N; j++) {
202                         subValues.add(v);
203                         v += step;
204                     }
205                 }
206                 subValues.add(ticksValue.get(ticksValue.size() - 1));
207             }
208         }
209
210         return subValues;
211     }
212
213     @Override
214     public String toString() {
215         String lowerBoundBracket;
216         String upperBoundBracket;
217
218         if (isLowerBoundIncluded) {
219             lowerBoundBracket = LEFT_BRACKET;
220         } else {
221             lowerBoundBracket = RIGHT_BRACKET;
222         }
223
224         if (isUpperBoundIncluded) {
225             upperBoundBracket = RIGHT_BRACKET;
226         } else {
227             upperBoundBracket = LEFT_BRACKET;
228         }
229         return "Graduation " + lowerBoundBracket
230                + getFormat().format(lowerBound) + ", "
231                + getFormat().format(upperBound) + upperBoundBracket;
232     }
233 }