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 public class LogarithmicTickCalculator implements TickCalculator
29 {
30
31
32
33
34
35
36
37 public LogarithmicTickCalculator(int tickSpacing, boolean boundTicksVisible)
38 {
39
40 this.tickSpacing = tickSpacing;
41 this.boundTicksVisible = boundTicksVisible;
42 }
43
44 private int tickSpacing;
45 private boolean boundTicksVisible;
46
47 private Tick[] internalCalculateTicks(int length, TickParameters measurer, Range range, RangedValue rangedVal)
48 {
49 if (measurer == null) measurer = new DefaultTickParameters(50, "%0.4g");
50 double min = rangedVal.getMinimum();
51 double max = rangedVal.getMaximum();
52 double tickFactorLog = 1;
53 double rmaxLog = Math.floor(logg(1.00000001*max));
54 double rminLog = Math.ceil(logg(0.99999999*min));
55 double tickSpace;
56 int nticks;
57 boolean minorTicksDrawn;
58
59
60 if(Math.pow(10, rmaxLog) < min || Math.pow(10, rminLog) > max) {
61 int i;
62 double x;
63 double step = Math.pow(10, rmaxLog);
64 Tick[] ticks;
65
66 for(x = step; x < min; x += step) {
67 ;
68 }
69
70 rminLog = Math.round(x / step) * step;
71
72 for(; x <= max; x += step) {
73 ;
74 }
75
76 rmaxLog = Math.round(x / step - 1) * step;
77
78 nticks = (int)Math.round((rmaxLog - rminLog) / step + 1);
79
80 if(nticks == 1) {
81 return onlyOneTick(rminLog, boundTicksVisible, min, max, range, rangedVal);
82 }
83
84 tickSpace = (range.toRelative(rmaxLog,rangedVal)
85 - range.toRelative(rmaxLog - step,rangedVal)) * length;
86
87 if(tickSpace > tickSpacing) {
88 ticks = (boundTicksVisible ? new Tick[nticks + 2]
89 : new Tick[nticks]);
90
91 if(boundTicksVisible) {
92
93 ticks[0] = new Tick(min, range.toRelative(min,rangedVal), true, "");
94
95 for(i = 1, x = rminLog; x <= rmaxLog; x += step, i++) {
96 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
97 }
98
99 ticks[nticks + 1] = new Tick(max, range.toRelative(max,rangedVal), true, "");
100
101 return ticks;
102 } else {
103 for(i = 0, x = rminLog; x <= rmaxLog; x += step, i++) {
104 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
105 }
106
107 return ticks;
108 }
109 }
110
111 step *= 2.0;
112
113 for(x = step; x < min; x += step) {
114 ;
115 }
116
117 rminLog = Math.round(x / step) * step;
118
119 for(; x <= max; x += step) {
120 ;
121 }
122
123 rmaxLog = Math.round(x / step - 1) * step;
124 nticks = (int)Math.round((rmaxLog - rminLog) / step + 1);
125
126 if(nticks == 1) {
127 return onlyOneTick(rminLog, boundTicksVisible, min, max, range, rangedVal);
128 }
129
130 tickSpace = (range.toRelative(rmaxLog,rangedVal)
131 - range.toRelative(rmaxLog - step,rangedVal)) * length;
132
133 if(tickSpace > tickSpacing) {
134 ticks = (boundTicksVisible ? new Tick[nticks + 2]
135 : new Tick[nticks]);
136
137 if(boundTicksVisible) {
138
139 ticks[0] = new Tick(min, range.toRelative(min,rangedVal), true, "");
140
141 for(i = 1, x = rminLog; x <= rmaxLog; x += step, i++) {
142 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
143 }
144
145
146 ticks[nticks + 1] = new Tick(max, range.toRelative(max,rangedVal), true, "");
147
148 return ticks;
149 } else {
150 for(i = 0, x = rminLog; x <= rmaxLog; x += step, i++) {
151 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
152 }
153
154 return ticks;
155 }
156 }
157
158 step = Math.pow(10, Math.floor(logg(min)));
159
160 if(min <= 5 * step && max >= 5 * step) {
161 rminLog = 5 * step;
162
163 return onlyOneTick(rminLog, boundTicksVisible, min, max, range, rangedVal);
164 } else {
165 ticks = new Tick[0];
166
167 return ticks;
168 }
169 }
170
171
172 nticks = (int)((rmaxLog - rminLog) / tickFactorLog) + 1;
173 tickSpace = (range.toRelative(Math.pow(10, rmaxLog),rangedVal)
174 - range.toRelative(Math.pow(10, rminLog),rangedVal)) * length / nticks;
175
176
177 while(tickSpace < tickSpacing || nticks > 25) {
178 tickFactorLog++;
179 rminLog = rmaxLog
180 - Math.floor((rmaxLog - rminLog) / tickFactorLog) * tickFactorLog;
181
182 nticks = (int)((rmaxLog - rminLog) / tickFactorLog) + 1;
183 tickSpace = (range.toRelative(Math.pow(10, rmaxLog),rangedVal)
184 - range.toRelative(Math.pow(10, rminLog),rangedVal)) * length / nticks;
185
186 if(rminLog == rmaxLog) {
187 break;
188 }
189 }
190
191
192 if(rminLog == rmaxLog) {
193 double r = Math.max(Math.pow(10, rminLog) - min,
194 0.1 * (max - Math.pow(10, rmaxLog)));
195 double step;
196
197 if(r < 0.1) {
198 if(boundTicksVisible) {
199 return new Tick[] {
200 new Tick(min, 0, true, ""), new Tick(max, 1, true, "")
201 };
202 } else {
203 return new Tick[] {
204 new Tick(rminLog, range.toRelative(Math.pow(10, rminLog),rangedVal), true,
205 measurer.formatNumber(Math.pow(10, rminLog)))
206 };
207 }
208 }
209
210 minorTicksDrawn = (range.toRelative(Math.pow(10, rminLog),rangedVal)
211 - range.toRelative(0.9 * Math.pow(10, rminLog),rangedVal)) * length > tickSpacing;
212
213 double majorTick = Math.pow(10, rminLog);
214 step = 0.1 * majorTick;
215
216 double x;
217
218 for(x = Math.pow(10, rminLog); x >= min; x -= step) {
219 ;
220 }
221
222 rminLog = x + step;
223
224 for(x = Math.pow(10, rminLog); x <= max; x += step) {
225 ;
226 }
227
228 rmaxLog = x - step;
229 nticks = (int)Math.round((rmaxLog - rminLog) / step + 1);
230
231 Tick[] ticks = new Tick[nticks];
232 int i;
233
234 for(i = 0, x = rminLog; x < majorTick; x += step, i++) {
235 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
236 }
237
238 ticks[i++] = new Tick(x, range.toRelative(x,rangedVal), true,
239 measurer.formatNumber(x));
240 step *= 10;
241 x += step;
242
243 for(; x <= max; x += step, i++) {
244 ticks[i] = new Tick(x, range.toRelative(x,rangedVal), false);
245 }
246
247 if(boundTicksVisible) {
248 Tick[] ticks2 = new Tick[nticks + 2];
249
250 ticks2[0] = new Tick(min, 0, true, "");
251
252 for(i = 1; i <= nticks; i++) {
253 ticks2[i] = ticks[i - 1];
254 }
255
256 ticks2[i] = new Tick(max, 1.0, true, "");
257
258 return ticks2;
259 }
260
261 return ticks;
262 }
263 double rminLLog = rminLog;
264 double rmaxLLog = rmaxLog;
265 double tickFactorLLog = tickFactorLog;
266
267
268 double x;
269
270 while(true) {
271 for(x = rminLLog; x < rmaxLLog - 0.1; x += tickFactorLLog) {
272 if(measurer.measureTick(range.toRelative(Math.pow(10, x),rangedVal),
273 measurer.formatNumber(Math.pow(10, x)))
274 + measurer.measureTick(range.toRelative(Math.pow(10,
275 x + tickFactorLLog),rangedVal),
276 measurer.formatNumber(Math.pow(10, x + tickFactorLLog))) > 1.5 * (range
277 .toRelative(Math.pow(10, x + tickFactorLLog),rangedVal)
278 - range.toRelative(Math.pow(10, x),rangedVal)) * length) {
279 break;
280 }
281 }
282
283 if(x >= rmaxLLog) {
284 break;
285 } else {
286 tickFactorLLog = (tickFactorLog == 1 ? tickFactorLLog + 1
287 : 2 * tickFactorLLog);
288 rminLLog = rmaxLLog
289 - Math.floor((rmaxLLog - rminLog) / tickFactorLLog) * tickFactorLLog;
290 }
291 }
292
293 if((rmaxLLog - rminLLog) / tickFactorLLog + 1 > 12) {
294 tickFactorLLog++;
295 rminLLog = rmaxLLog
296 - Math.floor((rmaxLLog - rminLLog) / tickFactorLLog) * tickFactorLLog;
297 }
298
299 int factor = 1;
300 minorTicksDrawn = (range.toRelative(Math.pow(10, rmaxLog),rangedVal)
301 - range.toRelative(0.9 * Math.pow(10, rmaxLog),rangedVal)) * length > tickSpacing
302 && Math.round(tickFactorLog) == 1;
303
304 if(minorTicksDrawn) {
305 ;
306 } else if((range.toRelative(Math.pow(10, rmaxLog),rangedVal)
307 - range.toRelative(0.8 * Math.pow(10, rmaxLog),rangedVal)) * length > tickSpacing
308 && Math.round(tickFactorLog) == 1) {
309 minorTicksDrawn = true;
310 factor = 2;
311 } else if((range.toRelative(Math.pow(10, rmaxLog),rangedVal)
312 - range.toRelative(0.5 * Math.pow(10, rmaxLog),rangedVal)) * length > tickSpacing
313 && Math.round(tickFactorLog) == 1) {
314 minorTicksDrawn = true;
315 factor = 5;
316 }
317
318 double step;
319 double rmin;
320 double rmax;
321
322 if(minorTicksDrawn) {
323 rmax = Math.pow(10, rmaxLog + 1);
324 step = rmax * factor / 10;
325
326 while(rmax > 1.00000001*max) {
327 rmax -= step;
328 }
329
330 rmin = Math.pow(10, rminLog - 1) * factor;
331 step = rmin;
332
333 while(rmin < 0.99999999*min) {
334 rmin += step;
335 }
336 nticks = (int)Math.round((factor == 1 ? 9 : 10 / factor) * (rmaxLog
337 - rminLog) + 1
338 + rmax / (factor * Math.pow(10,
339 rmaxLog))- (factor == 1 ? 1 : 0)
340 + (Math.pow(10, rminLog) - rmin) / (factor * Math.pow(10,
341 rminLog - 1)));
342
343 } else {
344 nticks = (int)((rmaxLog - rminLog) / tickFactorLog + 1);
345 rmin = Math.pow(10, rminLog);
346 step = rmin;
347 }
348 Tick[] ticks = new Tick[nticks];
349
350 int kmax = (int)Math.round(10.0 / factor);
351 int kmin = (factor == 1 ? 2 : 1);
352 int i = 0;
353
354 for(x = rmin; x < 0.95 * Math.pow(10, rminLog) && i < nticks;
355 x += step, i++) {
356 ticks[i] = new Tick(x,range.toRelative(x,rangedVal), false);
357 }
358
359 double xlog = Math.round(rminLog);
360
361 for(; xlog < rminLLog - 0.1 && i < nticks; xlog += tickFactorLog) {
362 x = Math.pow(10, xlog);
363 ticks[i++] = new Tick(x,range.toRelative(x,rangedVal), true, "");
364
365 if(minorTicksDrawn) {
366 step = factor * x;
367 x = (factor == 1 ? x + step : step);
368
369 for(int k = kmin; k < kmax && i < nticks;
370 k++, i++, x += step) {
371 ticks[i] = new Tick(x,range.toRelative(x,rangedVal), false);
372 }
373 }
374 }
375
376 int p = (int)Math.round(tickFactorLLog / tickFactorLog);
377 xlog = Math.round(rminLLog);
378
379 for(; i < nticks; xlog += tickFactorLog) {
380 x = Math.pow(10, xlog);
381
382 if(p == Math.round(tickFactorLLog / tickFactorLog)) {
383 ticks[i++] = new Tick(x,range.toRelative(x,rangedVal), true,
384 measurer.formatNumber(x));
385 p = 1;
386 } else {
387 ticks[i++] = new Tick(x,range.toRelative(x,rangedVal), true, "");
388 p++;
389 }
390
391 if(minorTicksDrawn) {
392 step = factor * x;
393 x = (factor == 1 ? x + step : step);
394
395 for(int k = kmin; k < kmax && i < nticks;
396 k++, i++, x += step) {
397 ticks[i] = new Tick(x,range.toRelative(x,rangedVal), false);
398 }
399 }
400 }
401
402 if(boundTicksVisible) {
403 Tick[] ticks2 = new Tick[nticks + 2];
404
405 ticks2[0] = new Tick(min,0, true, "");
406
407 for(i = 1; i <= nticks; i++) {
408 ticks2[i] = ticks[i - 1];
409 }
410
411 ticks2[i] = new Tick(max, 1, true, "");
412 return ticks2;
413 }
414
415 return ticks;
416 }
417
418 private Tick[] onlyOneTick(double x, boolean boundVisible, double min, double max, Range range, RangedValue rangedVal)
419 {
420 Tick[] ticks = (boundVisible ? new Tick[3] : new Tick[1]);
421
422 if(boundVisible) {
423 ticks[0] = new Tick(min,range.toRelative(min,rangedVal), true, "");
424 ticks[1] = new Tick(x,range.toRelative(x,rangedVal), false);
425 ticks[2] = new Tick(max,range.toRelative(max,rangedVal), true, "");
426
427 return ticks;
428 }
429
430 ticks[0] = new Tick(x,range.toRelative(x,rangedVal), false);
431
432 return ticks;
433 }
434
435 private double logg(double x)
436 {
437 return Math.log(x) / Math.log(10);
438 }
439
440
441
442
443
444 public Tick[] calculateTicks(int length, Range range, RangedValue rangedValue) {
445 return calculateTicks(length, new DefaultTickParameters(50, "%0.4g"), range, rangedValue);
446 }
447
448
449
450
451
452 public Tick[] calculateTicks(int length, TickParameters measurer, Range range, RangedValue rangedValue) {
453 return internalCalculateTicks(length, measurer, range, rangedValue);
454 }
455
456 }
457
458