1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.cosylab.gui.components.range2;
21
22
23
24
25
26
27
28
29 public class LinearTickCalculator implements TickCalculator
30 {
31
32 private static class TickStep
33 {
34
35 public static final double[] FACTORS = { 2, 2.5, 2 };
36
37 private double tep;
38
39 private boolean multipliedBefore;
40 private int i;
41
42
43
44
45 public void divStep()
46 {
47 if (multipliedBefore) {
48 i = change(-1);
49 multipliedBefore = false;
50 }
51
52 tep /= FACTORS[i];
53 i = change(-1);
54 }
55
56
57
58
59 public void mulStep()
60 {
61 if (!multipliedBefore) {
62 i = change(1);
63 multipliedBefore = true;
64 }
65
66 tep *= FACTORS[i];
67 i = change(+1);
68 }
69
70
71
72
73
74
75
76
77
78
79 public TickStep(double step, int start, boolean multipliedBefore)
80 {
81 i = start;
82 this.tep = step;
83 this.multipliedBefore = multipliedBefore;
84 }
85
86 private int change(int di)
87 {
88 if (i == FACTORS.length - 1 && di == 1) {
89 return 0;
90 }
91
92 if (i == 0 && di == -1) {
93 return 2;
94 }
95
96 return i + di;
97 }
98 }
99
100 private int tickSpacing;
101 private boolean boundTicksVisible;
102
103
104
105
106
107
108
109 public LinearTickCalculator(int tickSpacing, boolean boundTicksVisible)
110 {
111 this.tickSpacing = tickSpacing;
112 this.boundTicksVisible = boundTicksVisible;
113 }
114
115
116
117
118
119
120
121
122 private Tick[] internalCalculateTicks(int length, TickParameters measurer, Range range, RangedValue rangedVal)
123 {
124 if (measurer == null) measurer = new DefaultTickParameters(50, "%0.6g");
125 double min = rangedVal.getMinimum();
126 double max = rangedVal.getMaximum();
127 TickStep s = new TickStep(getRStep(min, max), 0, true);
128 boolean zeroOnScale = isInRange(0, min, max);
129
130 double rmin = getRmin(min, s, zeroOnScale);
131 double rmax = getRmax(rmin, max, s);
132
133 int nticks = (int)((rmax - rmin) / s.tep) + 1;
134
135 double tickSpace = (range.toRelative(rmax,rangedVal)
136 - range.toRelative(rmax - s.tep,rangedVal)) * length;
137
138
139 while (tickSpace != 0 && nticks < 6
140 s.divStep();
141 rmin = getRmin(min, s, zeroOnScale);
142 rmax = getRmax(rmin,max, s);
143 nticks = (int)((rmax - rmin) / s.tep) + 1;
144 tickSpace = (range.toRelative(rmax,rangedVal)
145 - range.toRelative(rmax - s.tep,rangedVal)) * length;
146
147
148
149 }
150
151
152 while (tickSpace < tickSpacing || nticks > 25) {
153 s.mulStep();
154 rmin = getRmin(rmin, s, zeroOnScale);
155 rmax = getRmax(rmin, max, s);
156
157 if (rmin == rmax || Double.isNaN(rmin) || Double.isNaN(rmax)
158 || Double.isInfinite(rmin) || Double.isInfinite(rmax)) {
159 if (boundTicksVisible) {
160 return new Tick[]{
161 new Tick(min, 0, true, ""), new Tick(max, 1, true, "")
162 };
163 } else if (zeroOnScale) {
164 return new Tick[]{
165 new Tick(0,range.toRelative(0,rangedVal), true,
166 measurer.formatNumber(0))
167 };
168 } else {
169 return new Tick[]{
170 new Tick(rmin,range.toRelative(rmin,rangedVal), true,
171 measurer.formatNumber(rmin))
172 };
173 }
174 }
175
176 nticks = (int)((rmax - rmin) / s.tep) + 1;
177 tickSpace = (range.toRelative(rmax,rangedVal)
178 - range.toRelative(rmax - s.tep,rangedVal)) * length;
179 }
180
181
182 double rstep = s.tep;
183 TickStep ls = new TickStep(s.tep, s.i, s.multipliedBefore);
184 double rminL = rmin;
185 double rmaxL = rmax;
186
187
188 double x;
189
190 while (true) {
191
192 for (x = rminL; x < rmaxL - ls.tep / 10; x += ls.tep) {
193 if (measurer.measureTick(range.toRelative(x,rangedVal),
194 measurer.formatNumber(x))
195 + measurer.measureTick(range.toRelative(x + ls.tep,rangedVal),
196 measurer.formatNumber(x + ls.tep)) > 1.3 * (range
197 .toRelative(x + ls.tep,rangedVal) - range.toRelative(x,rangedVal)) * length) {
198 break;
199 }
200 }
201
202 if (x >= rmaxL - ls.tep / 10) {
203 break;
204 } else {
205 ls.mulStep();
206 rminL = getRmin(min, ls, zeroOnScale);
207 rmaxL = getRmax(rminL, max, ls);
208 }
209 }
210
211 if ((int)((rmaxL - rminL) / ls.tep) + 1 > 12) {
212 ls.mulStep();
213 rminL = getRmin(min, ls, zeroOnScale);
214 rmaxL = getRmax(rminL, max, ls);
215 }
216
217 rstep = ls.tep / 2;
218 rmin = getRmin(min, new TickStep(rstep, 0, true), zeroOnScale);
219 rmax = getRmax(rmin, max, new TickStep(rstep, 0, true));
220
221
222 double minorStep = rstep / 5;
223 double minorMin = getRmin(min, new TickStep(minorStep, 0, true), zeroOnScale);
224 double minorMax = getRmax(minorMin, max, new TickStep(minorStep, 0, true));
225
226 if ((range.toRelative(minorMax,rangedVal)
227 - range.toRelative(minorMax - minorStep,rangedVal)) * length > tickSpacing) {
228
229 } else {
230 minorStep = rstep / 2;
231 minorMin = getRmin(min, new TickStep(minorStep, 0, true), zeroOnScale);
232 minorMax = getRmax(minorMin, max, new TickStep(minorStep, 0, true));
233
234 if ((range.toRelative(minorMax,rangedVal)
235 - range.toRelative(minorMax - minorStep,rangedVal)) * length > tickSpacing) {
236
237 } else {
238 minorStep = rstep;
239 }
240 }
241
242 minorMin = getRmin(min, new TickStep(minorStep, 0, true), zeroOnScale);
243 minorMax = getRmax(minorMin, max, new TickStep(minorStep, 0, true));
244
245 nticks = Math.round(Math.round((rmax - rmin) / rstep) * Math.round(rstep / minorStep)
246 + Math.round((minorMax - rmax + rmin - minorMin) / minorStep)) + 1;
247
248 if (nticks == 0) {
249 Tick[] ticks = new Tick[1];
250 ticks[0] = new Tick(0,range.toRelative(0,rangedVal), true, measurer.formatNumber(0));
251
252 return ticks;
253 }
254
255 int i = 0;
256 int di = 0;
257
258 if (boundTicksVisible) {
259 if ((rmin - min) / (max - min) > 0.01
260 || (max - rmin) / (max - min) > 0.01) {
261 nticks += 2;
262 i = 1;
263 di = 1;
264 }
265 }
266
267 Tick[] ticks = new Tick[nticks];
268
269 if (i == 1) {
270
271 ticks[0] = new Tick(rmin, 0.0, true, "");
272 }
273
274 int ratio2 = (int)Math.round(ls.tep / minorStep);
275 int ratio1 = (int)Math.round(rstep / minorStep);
276 x = minorMin;
277
278 for (; x < rmin - minorStep / 10; i++, x += minorStep) {
279 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
280 }
281
282 int cont = 0;
283
284 for (int k = 0; x < rminL - minorStep / 10; i++, k++, x += minorStep) {
285 if (k % ratio1 == 0) {
286 x = rmin + (cont++) * rstep;
287 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), true, "");
288 } else {
289 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
290 }
291 }
292
293 for (int k = 0; i < nticks - di; i++, k++, x += minorStep) {
294 if (k % ratio2 == 0) {
295 x = rmin + (cont++) * rstep;
296 x = (Math.abs(x) != 0.0 ? x : Math.abs(x));
297 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), true,
298
299 } else if (k % ratio1 == 0) {
300 x = rmin + (cont++) * rstep;
301 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), true, "");
302 } else {
303 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
304 }
305 }
306
307 if (di == 1) {
308
309 ticks[i] = new Tick(rmax, 1.0, true, "");
310 }
311
312 return ticks;
313 }
314
315 private double getRmin(double min, TickStep s, boolean zeroOnScale)
316 {
317 double rmin;
318
319 if (zeroOnScale) {
320 rmin = -Math.floor(-min / s.tep) * s.tep;
321
322 if (rmin == -0.0) {
323 rmin = 0.0;
324 }
325 } else {
326 rmin = (min < 0 ? -Math.floor(-min / s.tep) * s.tep
327 : Math.ceil(min / s.tep) * s.tep);
328 }
329
330 return rmin;
331 }
332
333 private double getRmax(double rmin, double max, TickStep s)
334 {
335
336 return rmin + Math.floor((float)((max - rmin) / s.tep)) * s.tep;
337 }
338
339 private double getRStep(double min, double max)
340 {
341 double rstep;
342
343 if (Math.abs(max) > Math.abs(min)) {
344 rstep = Math.pow(10,
345 Math.floor(Math.log(Math.abs(max)) / Math.log(10)));
346 } else {
347 rstep = Math.pow(10,
348 Math.floor(Math.log(Math.abs(min)) / Math.log(10)));
349 }
350
351 return rstep;
352 }
353
354 private boolean isInRange(double value, double min, double max)
355 {
356 return (value >= min && value <= max);
357 }
358
359
360
361
362
363 public Tick[] calculateTicks(int length, Range range, RangedValue rangedValue) {
364 return calculateTicks(length, new DefaultTickParameters(50, "%0.6g"), range, rangedValue);
365 }
366
367
368
369
370
371 public Tick[] calculateTicks(int length, TickParameters measurer, Range range, RangedValue rangedValue) {
372 return internalCalculateTicks(length, measurer, range, rangedValue);
373 }
374 }
375
376