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