cgv
Loading...
Searching...
No Matches
ticks.cxx
1#include "ticks.h"
2
3#include <algorithm>
4#include <cmath>
5#include <cgv/math/compare_float.h>
6
7namespace cgv {
8namespace media {
9
10std::tuple<int, int, int> get_tick_specification(float start, float stop, int count) {
11 const float e10 = std::sqrt(50.0f);
12 const float e5 = std::sqrt(10.0f);
13 const float e2 = std::sqrt(2.0f);
14
15 const float step = (stop - start) / std::max(0, count);
16 const float power = std::floor(std::log10(step));
17 const float error = step / std::pow(10.0f, power);
18 const float factor = error >= e10 ? 10.0f : error >= e5 ? 5.0f : error >= e2 ? 2.0f : 1.0f;
19 int i1, i2;
20 float inc;
21 if(power < 0) {
22 inc = std::pow(10.0f, -power) / factor;
23 i1 = static_cast<int>(std::round(start * inc));
24 i2 = static_cast<int>(std::round(stop * inc));
25 if(i1 / inc < start) ++i1;
26 if(i2 / inc > stop) --i2;
27 inc = -inc;
28 } else {
29 inc = std::pow(10.0f, power) * factor;
30 i1 = static_cast<int>(std::round(start / inc));
31 i2 = static_cast<int>(std::round(stop / inc));
32 if(i1 * inc < start) ++i1;
33 if(i2 * inc > stop) --i2;
34 }
35 if(i2 < i1 && 0.5 <= count && count < 2)
36 return get_tick_specification(start, stop, count * 2);
37 return { i1, i2, static_cast<int>(inc) };
38};
39
40std::vector<float> compute_ticks(float start, float stop, int count) {
41 if(count <= 0)
42 return {};
43
44 if(start == stop)
45 return { start };
46
47 const bool reverse = stop < start;
48 if(reverse)
49 std::swap(start, stop);
50
51 float i1, i2, inc;
52 std::tie(i1, i2, inc) = get_tick_specification(start, stop, count);
53
54 if(i2 < i1)
55 return {};
56
57 const int n = i2 - i1 + 1;
58 std::vector<float> ticks(n);
59 if(reverse) {
60 if(inc < 0)
61 for(int i = 0; i < n; ++i)
62 ticks[i] = static_cast<float>(i2 - i) / static_cast<float>(-inc);
63 else
64 for(int i = 0; i < n; ++i)
65 ticks[i] = static_cast<float>(i2 - i) * static_cast<float>(inc);
66 } else {
67 if(inc < 0)
68 for(int i = 0; i < n; ++i)
69 ticks[i] = static_cast<float>(i1 + i) / static_cast<float>(-inc);
70 else
71 for(int i = 0; i < n; ++i)
72 ticks[i] = static_cast<float>(i1 + i) * static_cast<float>(inc);
73 }
74 return ticks;
75};
76
77std::vector<float> compute_ticks_log(float start, float stop, float base, int count) {
78 const bool crosses_zero = std::signbit(start) != std::signbit(stop);
79
80 if(count <= 0 || crosses_zero || cgv::math::is_zero(start) || cgv::math::is_zero(stop))
81 return {};
82
83 if(start == stop)
84 return { start };
85
86 const bool reverse = stop < start;
87 if(reverse)
88 std::swap(start, stop);
89
90 const float log_start = std::log(start) / std::log(base);
91 const float log_stop = std::log(stop) / std::log(base);
92 const bool base_is_integral = cgv::math::is_equal(base, std::floor(base));
93
94 std::vector<float> ticks;
95 if(base_is_integral && static_cast<int>(std::ceil(log_stop - log_start)) < count) {
96 int int_base = static_cast<int>(base);
97 int i = static_cast<int>(std::floor(log_start));
98 int j = static_cast<int>(std::ceil(log_stop));
99 if(start > 0) {
100 for(; i <= j; ++i) {
101 for(int k = 1; k < int_base; ++k) {
102 float t = i < 0 ? k / std::pow(base, -i) : k * std::pow(base, i);
103 if(t < start) continue;
104 if(t > stop) break;
105 ticks.push_back(t);
106 }
107 }
108 } else {
109 for(; i <= j; ++i) {
110 for(int k = int_base - 1; k >= 1; --k) {
111 float t = i > 0 ? k / std::pow(base, -i) : k * std::pow(base, i);
112 if(t < start) continue;
113 if(t > stop) break;
114 ticks.push_back(t);
115 }
116 }
117 }
118 if(ticks.size() * 2 < count)
119 ticks = compute_ticks(start, stop, count);
120 } else {
121 ticks = compute_ticks(log_start, log_stop, std::min(static_cast<int>(std::ceil(log_stop - log_start)), count));
122 for(float& tick : ticks)
123 tick = std::pow(base, tick);
124 }
125
126 if(reverse)
127 std::reverse(ticks.begin(), ticks.end());
128
129 return ticks;
130}
131
132} // namespace media
133} // namespace cgv
this header is dependency free
Definition print.h:11