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