FuncSketch
Loading...
Searching...
No Matches
axis_ticks.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2026 MusicScience37 (Kenta Kabashima)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
21
22#include <algorithm>
23#include <cassert>
24#include <cmath>
25#include <cstdint>
26#include <ranges>
27
28#include <fmt/format.h>
29
30namespace func_sketch::plotter {
31
32void generate_axis_ticks(const std::pair<Real, Real>& range,
33 std::size_t approx_num_ticks, AxisTicks& ticks) {
34 constexpr std::size_t min_approx_num_ticks = 3;
35 approx_num_ticks = std::max(approx_num_ticks, min_approx_num_ticks);
36
37 const auto [min_value, max_value] = range;
38 const Real range_size = max_value - min_value;
39 // Ranges should be checked in the PlotRange class, so only assert here.
40 assert(range_size > 0.0);
41 const Real approx_tick_interval =
42 range_size / static_cast<Real>(approx_num_ticks - 1);
43
44 auto tick_interval_digits =
45 static_cast<int>(std::floor(std::log10(approx_tick_interval)));
46 Real tick_interval_order = std::pow(10.0, tick_interval_digits);
47 Real tick_interval_number = approx_tick_interval / tick_interval_order;
48 // Round tick interval to "nice numbers" (1, 2, 5, 10, ...).
49 if (tick_interval_number < 1.5) {
50 tick_interval_number = 1.0;
51 } else if (tick_interval_number < 3.5) {
52 tick_interval_number = 2.0;
53 } else if (tick_interval_number < 7.5) {
54 tick_interval_number = 5.0;
55 } else {
56 tick_interval_number = 1.0;
57 tick_interval_digits += 1;
58 tick_interval_order *= 10.0;
59 }
60 const Real tick_interval = tick_interval_number * tick_interval_order;
61
62 using TickIndexType =
63 std::int_fast64_t; // Prevent overflow in conversions from double.
64 const auto min_tick_index =
65 static_cast<TickIndexType>(std::ceil(min_value / tick_interval));
66 const auto max_tick_index =
67 static_cast<TickIndexType>(std::floor(max_value / tick_interval));
68 const auto num_ticks =
69 static_cast<std::size_t>(max_tick_index - min_tick_index + 1);
70 ticks.values.resize(num_ticks);
71 ticks.strings.resize(num_ticks);
72
73 // Generate tick values.
74 std::ranges::copy(std::views::iota(min_tick_index, max_tick_index + 1) |
75 std::views::transform([tick_interval](TickIndexType index) {
76 return static_cast<Real>(index) * tick_interval;
77 }),
78 ticks.values.begin());
79
80 // Format tick values according to the order of magnitude.
81 const int tick_digits = static_cast<int>(std::floor(
82 std::log10(std::max(std::abs(min_value), std::abs(max_value)))));
83 const Real tick_order = std::pow(10.0, tick_digits);
84 constexpr int digits_threshold_for_scientific_notation = 4;
85 if (std::abs(tick_digits) >= digits_threshold_for_scientific_notation) {
86 const int tick_digits_after_decimal_point =
87 std::max(tick_digits - tick_interval_digits, 0);
88 std::ranges::transform(ticks.values, ticks.strings.begin(),
89 [tick_order, tick_digits, tick_digits_after_decimal_point](
90 Real value) {
91 const Real tick_number = value / tick_order;
92 return fmt::format("{:.{}f}e{:+}", tick_number,
93 tick_digits_after_decimal_point, tick_digits);
94 });
95 } else {
96 const int tick_digits_after_decimal_point =
97 std::max(-tick_interval_digits, 0);
98 std::ranges::transform(ticks.values, ticks.strings.begin(),
99 [tick_digits_after_decimal_point](Real value) {
100 return fmt::format(
101 "{:.{}f}", value, tick_digits_after_decimal_point);
102 });
103 }
104}
105
106} // namespace func_sketch::plotter
Definition of AxisTicks class.
void generate_axis_ticks(const std::pair< Real, Real > &range, std::size_t approx_num_ticks, AxisTicks &ticks)
Generate ticks of axes.
double Real
Type of real numbers in this project.
Struct to represent ticks of axes.
Definition axis_ticks.h:34
std::vector< std::string > strings
Tick strings.
Definition axis_ticks.h:39
std::vector< Real > values
Tick values in real numbers.
Definition axis_ticks.h:36