71 lines
2.8 KiB
C++
71 lines
2.8 KiB
C++
#include "Time_Axis.h"
|
|
|
|
#include "Axis_Format.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace renderive::detail {
|
|
namespace {
|
|
|
|
Time_Axis_State time_state_from(const Time_Axis_Properties& properties) {
|
|
Time_Axis_State state;
|
|
static_cast<Time_Axis_Properties&>(state) = properties;
|
|
return state;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Time_Axis_Control::Time_Axis_Control(Plot_Core& plot, const Time_Axis_Properties& properties)
|
|
: Axis_State_Strategy(plot, time_state_from(properties)) {}
|
|
|
|
std::size_t Time_Axis_Control::time_point_count() const {
|
|
return read([](const Time_Axis_State& state) { return state.samples.size(); });
|
|
}
|
|
|
|
int Time_Axis_Control::append_time(Time_Of_Day time) {
|
|
int tick{};
|
|
update([&](Time_Axis_State& state) {
|
|
tick = state.next_tick++;
|
|
state.samples.emplace_back(tick, time);
|
|
const auto limit = static_cast<std::size_t>(std::max(512, state.visible_count.get() * 4));
|
|
while (state.samples.size() > limit)
|
|
state.samples.pop_front();
|
|
});
|
|
return tick;
|
|
}
|
|
|
|
Time_Of_Day Time_Axis_Control::tick_to_time(int tick) const {
|
|
return read([tick](const Time_Axis_State& state) {
|
|
const auto iterator = std::find_if(state.samples.begin(), state.samples.end(),
|
|
[tick](const auto& value) { return value.first == tick; });
|
|
return iterator == state.samples.end() ? Time_Of_Day{} : iterator->second;
|
|
});
|
|
}
|
|
|
|
Range Time_Axis_Control::coordinate_range(const Time_Axis_State& state) const noexcept {
|
|
const int latest = std::max(1, state.next_tick - 1);
|
|
const int earliest = std::max(0, latest - state.visible_count.get() + 1);
|
|
return state.newest_at_start ? Range{static_cast<double>(latest), static_cast<double>(earliest)}
|
|
: Range{static_cast<double>(earliest), static_cast<double>(latest)};
|
|
}
|
|
|
|
double Time_Axis_Control::calculate_tick_step(Range range, const Time_Axis_State& state) const {
|
|
const double available = static_cast<double>(state.pixel_length);
|
|
const double label_width = std::max(48.0, state.unit_text_font.size * 7.0);
|
|
const double label_count =
|
|
std::max(1.0, available / (label_width + state.tick_label_spacing_px.get()));
|
|
return std::max(1.0, std::ceil(range.size() / label_count));
|
|
}
|
|
|
|
std::string Time_Axis_Control::format_tick_label(double tick, const Time_Axis_State& state) const {
|
|
const int target = static_cast<int>(std::llround(tick));
|
|
const auto iterator = std::find_if(state.samples.begin(), state.samples.end(),
|
|
[target](const auto& value) { return value.first == target; });
|
|
return iterator == state.samples.end()
|
|
? std::string{}
|
|
: formatted_axis_time(iterator->second, state.format);
|
|
}
|
|
|
|
} // namespace renderive::detail
|