ai改了半天还是不对
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "Axis.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
@@ -39,6 +40,71 @@ std::string numeric_label(double value, int precision) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ascii_iequals(std::string_view left, std::string_view right) {
|
||||
return left.size() == right.size() &&
|
||||
std::ranges::equal(left, right, [](char first, char second) {
|
||||
return std::tolower(static_cast<unsigned char>(first)) ==
|
||||
std::tolower(static_cast<unsigned char>(second));
|
||||
});
|
||||
}
|
||||
|
||||
std::string time_label(double seconds, int precision,
|
||||
std::string_view unit) {
|
||||
if (!unit.empty() && !ascii_iequals(unit, "s") &&
|
||||
!ascii_iequals(unit, "sec") && !ascii_iequals(unit, "second") &&
|
||||
!ascii_iequals(unit, "seconds"))
|
||||
return numeric_label(seconds, precision) + " " + std::string(unit);
|
||||
const auto sign = seconds < 0.0 ? "-" : "";
|
||||
const auto absolute = std::abs(seconds);
|
||||
if (absolute == 0.0) return "0 s";
|
||||
if (absolute < 1.0)
|
||||
return std::string(sign) +
|
||||
numeric_label(absolute * 1'000.0, precision) + " ms";
|
||||
if (absolute < 60.0)
|
||||
return std::string(sign) + numeric_label(absolute, precision) + " s";
|
||||
|
||||
const auto hours = static_cast<std::uint64_t>(absolute / 3'600.0);
|
||||
const auto minutes = static_cast<std::uint64_t>(absolute / 60.0) % 60U;
|
||||
const auto remaining = absolute -
|
||||
static_cast<double>(hours * 3'600U + minutes * 60U);
|
||||
std::ostringstream stream;
|
||||
stream << sign;
|
||||
if (hours != 0U) stream << hours << ':' << std::setfill('0') << std::setw(2);
|
||||
stream << minutes << ':' << std::setfill('0')
|
||||
<< std::setw(precision > 0 ? precision + 3 : 2)
|
||||
<< std::fixed << std::setprecision(std::clamp(precision, 0, 6))
|
||||
<< remaining;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string frequency_label(double frequency, int precision) {
|
||||
const auto magnitude = std::abs(frequency);
|
||||
double divisor{1.0};
|
||||
std::string_view unit{"Hz"};
|
||||
if (magnitude >= 1.0e9) {
|
||||
divisor = 1.0e9;
|
||||
unit = "GHz";
|
||||
}
|
||||
else if (magnitude >= 1.0e6) {
|
||||
divisor = 1.0e6;
|
||||
unit = "MHz";
|
||||
}
|
||||
else if (magnitude >= 1.0e3) {
|
||||
divisor = 1.0e3;
|
||||
unit = "kHz";
|
||||
}
|
||||
return numeric_label(frequency / divisor, precision) + " " +
|
||||
std::string(unit);
|
||||
}
|
||||
|
||||
std::string axis_tick_label(const Axis_Descriptor& axis, double coordinate) {
|
||||
if (axis.scale == Axis_Scale::time)
|
||||
return time_label(coordinate, axis.precision, axis.unit);
|
||||
if (ascii_iequals(axis.unit, "hz"))
|
||||
return frequency_label(coordinate, axis.precision);
|
||||
return numeric_label(coordinate, axis.precision);
|
||||
}
|
||||
|
||||
std::vector<Axis_Tick> linear_ticks(const Axis_Descriptor& axis) {
|
||||
const auto step = nice_tick_step(axis.range, axis.target_tick_count);
|
||||
if (!(step > 0.0) || !std::isfinite(step)) return {};
|
||||
@@ -49,7 +115,7 @@ std::vector<Axis_Tick> linear_ticks(const Axis_Descriptor& axis) {
|
||||
for (std::size_t index = 0; index < maximum_count; ++index) {
|
||||
const auto coordinate = first + static_cast<double>(index) * step;
|
||||
if (coordinate > maximum + step * 1.0e-9) break;
|
||||
ticks.push_back({coordinate, numeric_label(coordinate, axis.precision)});
|
||||
ticks.push_back({coordinate, axis_tick_label(axis, coordinate)});
|
||||
}
|
||||
if (axis.range.length() < 0.0) std::ranges::reverse(ticks);
|
||||
return ticks;
|
||||
@@ -73,7 +139,7 @@ std::vector<Axis_Tick> logarithmic_ticks(const Axis_Descriptor& axis) {
|
||||
const auto coordinate = multiple * decade;
|
||||
if (coordinate >= minimum * (1.0 - 1.0e-12) &&
|
||||
coordinate <= maximum * (1.0 + 1.0e-12))
|
||||
ticks.push_back({coordinate, numeric_label(coordinate, axis.precision)});
|
||||
ticks.push_back({coordinate, axis_tick_label(axis, coordinate)});
|
||||
}
|
||||
}
|
||||
const auto add_boundary = [&](double coordinate) {
|
||||
@@ -95,7 +161,7 @@ std::vector<Axis_Tick> logarithmic_ticks(const Axis_Descriptor& axis) {
|
||||
logarithmic_span < minimum_spacing)
|
||||
ticks.erase(nearest);
|
||||
}
|
||||
ticks.push_back({coordinate, numeric_label(coordinate, axis.precision)});
|
||||
ticks.push_back({coordinate, axis_tick_label(axis, coordinate)});
|
||||
}
|
||||
};
|
||||
add_boundary(minimum);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <plot/Axis.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace aethera::plot {
|
||||
namespace {
|
||||
|
||||
TEST(Axis_Formatting, Time_Scale_Uses_Elapsed_Time_Labels) {
|
||||
const Axis_Descriptor axis{
|
||||
{0.0, 4.0}, Axis_Scale::time, "Time", "s",
|
||||
5, 1, true, true, true};
|
||||
|
||||
const auto ticks = axis_ticks(axis);
|
||||
ASSERT_EQ(ticks.size(), 5U);
|
||||
EXPECT_EQ(ticks.front().label, "0 s");
|
||||
EXPECT_EQ(ticks[1].label, "1 s");
|
||||
EXPECT_EQ(ticks.back().label, "4 s");
|
||||
}
|
||||
|
||||
TEST(Axis_Formatting, Time_Scale_Formats_Long_Durations) {
|
||||
const Axis_Descriptor axis{
|
||||
{0.0, 4'000.0}, Axis_Scale::time, "Elapsed", "s",
|
||||
5, 0, true, true, true};
|
||||
|
||||
const auto ticks = axis_ticks(axis);
|
||||
ASSERT_FALSE(ticks.empty());
|
||||
EXPECT_EQ(ticks.front().label, "0 s");
|
||||
EXPECT_EQ(ticks.back().label, "1:06:40");
|
||||
}
|
||||
|
||||
TEST(Axis_Formatting, Hertz_Unit_Uses_Frequency_Prefixes) {
|
||||
const Axis_Descriptor axis{
|
||||
{10.0, 20'000.0}, Axis_Scale::logarithmic, "Frequency", "Hz",
|
||||
5, 0, true, true, true};
|
||||
|
||||
const auto ticks = axis_ticks(axis);
|
||||
const std::vector<std::string> labels = [&] {
|
||||
std::vector<std::string> result;
|
||||
result.reserve(ticks.size());
|
||||
for (const auto& tick : ticks) result.push_back(tick.label);
|
||||
return result;
|
||||
}();
|
||||
EXPECT_NE(std::ranges::find(labels, "10 Hz"), labels.end());
|
||||
EXPECT_NE(std::ranges::find(labels, "1 kHz"), labels.end());
|
||||
EXPECT_NE(std::ranges::find(labels, "20 kHz"), labels.end());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace aethera::plot
|
||||
Reference in New Issue
Block a user