25 thread_local std::vector<std::string> captured_warnings;
28 constexpr RealType kMatchTolerance = 1e-3;
29 constexpr RealType kIrrationalLookingTolerance = 5e-3;
31 constexpr std::array<RealType, 12> common_degree_angles = {0.0, 30.0, 45.0, 60.0, 90.0, 120.0,
32 135.0, 150.0, 180.0, 225.0, 270.0, 360.0};
34 constexpr std::array<RealType, 9> common_radian_angles = {0.0,
35 std::numbers::pi_v<RealType> / 6.0,
36 std::numbers::pi_v<RealType> / 4.0,
37 std::numbers::pi_v<RealType> / 3.0,
38 std::numbers::pi_v<RealType> / 2.0,
39 2.0 * std::numbers::pi_v<RealType> / 3.0,
40 3.0 * std::numbers::pi_v<RealType> / 4.0,
41 5.0 * std::numbers::pi_v<RealType> / 6.0,
42 std::numbers::pi_v<RealType>};
45 const RealType tolerance = kMatchTolerance)
noexcept
47 return std::abs(a - b) <= tolerance;
50 [[nodiscard]]
bool is_near_integer(
const RealType value)
noexcept
52 return is_close(value, std::round(value), 1e-6);
55 [[nodiscard]]
bool is_near_tenth(
const RealType value)
noexcept
57 return is_close(value * 10.0, std::round(value * 10.0), kIrrationalLookingTolerance * 10.0);
60 [[nodiscard]]
bool matches_common_angle(
const RealType abs_value,
61 const std::ranges::input_range
auto& candidates)
noexcept
63 for (
const RealType candidate : candidates)
65 if (is_close(abs_value, candidate))
73 [[nodiscard]]
bool matches_simple_pi_ratio(
const RealType abs_value)
noexcept
75 if (abs_value <= kEpsilon)
80 const RealType pi = std::numbers::pi_v<RealType>;
81 for (
int numerator = 1; numerator <= 12; ++numerator)
83 for (
int denominator = 1; denominator <= 12; ++denominator)
85 if (std::gcd(numerator, denominator) != 1)
92 if (is_close(abs_value, candidate))
101 [[nodiscard]]
RealType best_clean_trig_distance(
const RealType radians)
noexcept
103 constexpr std::array<RealType, 5> clean_values = {0.0, 0.5, std::numbers::sqrt2_v<RealType> / 2.0,
104 std::numbers::sqrt3_v<RealType> / 2.0, 1.0};
106 RealType best = std::numeric_limits<RealType>::max();
107 for (
const RealType value : {std::abs(std::sin(radians)), std::abs(std::cos(radians))})
109 for (
const RealType clean : clean_values)
111 best = std::min(best, std::abs(value - clean));
122 [[nodiscard]] std::string confidence_token(
const Confidence confidence)
141 const RealType abs_value = std::abs(value);
142 const RealType pi = std::numbers::pi_v<RealType>;
146 if (abs_value <= kEpsilon)
151 if (abs_value > 360.0)
155 else if (abs_value > 180.0)
159 else if (abs_value > two_pi)
163 else if (abs_value > pi)
168 const bool degree_famous = matches_common_angle(abs_value, common_degree_angles);
169 const bool radian_famous = matches_common_angle(abs_value, common_radian_angles);
170 if (degree_famous && !radian_famous)
174 else if (radian_famous && !degree_famous)
179 if (is_near_integer(abs_value))
181 const RealType rounded = std::round(abs_value);
182 if ((std::fmod(rounded, 45.0) == 0.0) || (std::fmod(rounded, 30.0) == 0.0) ||
183 (std::fmod(rounded, 15.0) == 0.0) || (std::fmod(rounded, 10.0) == 0.0) ||
184 (std::fmod(rounded, 5.0) == 0.0))
193 else if ((abs_value <= two_pi + 1.0) && !is_near_tenth(abs_value))
198 if (matches_simple_pi_ratio(abs_value))
206 const RealType degree_distance = best_clean_trig_distance(value * pi / 180.0);
207 const RealType radian_distance = best_clean_trig_distance(value);
208 if (degree_distance + 1e-4 < radian_distance)
212 else if (radian_distance + 1e-4 < degree_distance)
229 if ((lead >= 5) || ((max_score >= 7) && (lead >= 3)))
233 else if ((lead >= 3) || ((max_score >= 5) && (lead >= 2)))
237 else if ((lead >= 2) || (max_score >= 4))
264 std::vector<std::string> warnings = std::move(captured_warnings);
265 captured_warnings.clear();
270 const ValueKind kind,
const std::string_view source,
271 const std::string_view owner,
const std::string_view field)
279 const std::string message =
280 std::format(
"{} rotation {} '{}' looks like {} but '{}' was declared (confidence: {}, value: {}). "
281 "Change rotationangleunit or convert existing values.",
282 source, owner, field, unit_token(inference.
inferred_unit), unit_token(declared_unit),
283 confidence_token(inference.
confidence), value);
285 if (std::ranges::find(captured_warnings, message) == captured_warnings.end())
287 captured_warnings.push_back(message);