5#include <cgv/math/compare_float.h>
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);
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;
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;
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;
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) };
40std::vector<float> compute_ticks(
float start,
float stop,
int count) {
47 const bool reverse = stop < start;
49 std::swap(start, stop);
52 std::tie(i1, i2, inc) = get_tick_specification(start, stop, count);
57 const int n = i2 - i1 + 1;
58 std::vector<float> ticks(n);
61 for(
int i = 0; i < n; ++i)
62 ticks[i] =
static_cast<float>(i2 - i) /
static_cast<float>(-inc);
64 for(
int i = 0; i < n; ++i)
65 ticks[i] =
static_cast<float>(i2 - i) *
static_cast<float>(inc);
68 for(
int i = 0; i < n; ++i)
69 ticks[i] =
static_cast<float>(i1 + i) /
static_cast<float>(-inc);
71 for(
int i = 0; i < n; ++i)
72 ticks[i] =
static_cast<float>(i1 + i) *
static_cast<float>(inc);
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);
80 if(count <= 0 || crosses_zero || cgv::math::is_zero(start) || cgv::math::is_zero(stop))
86 const bool reverse = stop < start;
88 std::swap(start, stop);
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));
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));
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;
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;
118 if(ticks.size() * 2 < count)
119 ticks = compute_ticks(start, stop, count);
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);
127 std::reverse(ticks.begin(), ticks.end());
this header is dependency free