diff --git a/data/assets/examples/dashboarditems.asset b/data/assets/examples/dashboarditems.asset index 08e61ac06d..ba5138ba6b 100644 --- a/data/assets/examples/dashboarditems.asset +++ b/data/assets/examples/dashboarditems.asset @@ -52,6 +52,12 @@ local property_value = { DisplayString = "Earth is enabled: {}" } +local elapsed_time = { + Type = "DashboardItemElapsedTime", + Identifier = "ElapsedTime", + ReferenceTime = "2022-10-12 12:00:00" +} + asset.onInitialize(function() openspace.dashboard.addDashboardItem(angle) openspace.dashboard.addDashboardItem(date) @@ -61,9 +67,11 @@ asset.onInitialize(function() openspace.dashboard.addDashboardItem(parallel_connection) openspace.dashboard.addDashboardItem(mission) openspace.dashboard.addDashboardItem(property_value) + openspace.dashboard.addDashboardItem(elapsed_time) end) asset.onDeinitialize(function() + openspace.dashboard.removeDashboardItem(elapsed_time) openspace.dashboard.removeDashboardItem(property_value) openspace.dashboard.removeDashboardItem(mission) openspace.dashboard.removeDashboardItem(parallel_connection) @@ -74,6 +82,7 @@ asset.onDeinitialize(function() openspace.dashboard.removeDashboardItem(angle) end) +asset.export(elapsed_time) asset.export(property_value) asset.export(mission) asset.export(parallel_connection) diff --git a/include/openspace/util/timeconversion.h b/include/openspace/util/timeconversion.h index 38a4fc8f19..bf4caf076d 100644 --- a/include/openspace/util/timeconversion.h +++ b/include/openspace/util/timeconversion.h @@ -168,7 +168,10 @@ constexpr TimeUnit timeUnitFromString(std::string_view unitName) { } } -std::pair simplifyTime(double seconds, +std::pair simplifyTime(double seconds, + bool forceSingularForm = false); + +std::vector> splitTime(double seconds, bool forceSingularForm = false); constexpr double convertTime(double t, TimeUnit sourceUnit, TimeUnit destinationUnit) { diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index cb0f531ca0..bc5bb13686 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -28,6 +28,7 @@ set(HEADER_FILES dashboard/dashboarditemangle.h dashboard/dashboarditemdate.h dashboard/dashboarditemdistance.h + dashboard/dashboarditemelapsedtime.h dashboard/dashboarditemframerate.h dashboard/dashboarditemmission.h dashboard/dashboarditemparallelconnection.h @@ -82,6 +83,7 @@ set(SOURCE_FILES dashboard/dashboarditemangle.cpp dashboard/dashboarditemdate.cpp dashboard/dashboarditemdistance.cpp + dashboard/dashboarditemelapsedtime.cpp dashboard/dashboarditemframerate.cpp dashboard/dashboarditemmission.cpp dashboard/dashboarditemparallelconnection.cpp diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index 5dbbcd999e..a9d840a6d4 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,7 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) { fDashboard->registerClass("DashboardItemAngle"); fDashboard->registerClass("DashboardItemDate"); fDashboard->registerClass("DashboardItemDistance"); + fDashboard->registerClass("DashboardItemElapsedTime"); fDashboard->registerClass("DashboardItemFramerate"); fDashboard->registerClass("DashboardItemMission"); fDashboard->registerClass( diff --git a/modules/base/dashboard/dashboarditemdate.cpp b/modules/base/dashboard/dashboarditemdate.cpp index fa02763c3f..d585e4156b 100644 --- a/modules/base/dashboard/dashboarditemdate.cpp +++ b/modules/base/dashboard/dashboarditemdate.cpp @@ -39,7 +39,7 @@ namespace { constexpr openspace::properties::Property::PropertyInfo FormatStringInfo = { "FormatString", "Format String", - "The format text describing how this dashboard item renders it's text. This text " + "The format text describing how this dashboard item renders its text. This text " "must contain exactly one {} which is a placeholder that will contain the date" }; diff --git a/modules/base/dashboard/dashboarditemelapsedtime.cpp b/modules/base/dashboard/dashboarditemelapsedtime.cpp new file mode 100644 index 0000000000..8b54382c63 --- /dev/null +++ b/modules/base/dashboard/dashboarditemelapsedtime.cpp @@ -0,0 +1,184 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + constexpr openspace::properties::Property::PropertyInfo FormatStringInfo = { + "FormatString", + "Format String", + "The format text describing how this dashboard item renders its text. This text " + "must contain exactly one {} which is a placeholder that will contain the value " + "of the elapsed time." + }; + + constexpr openspace::properties::Property::PropertyInfo ReferenceTimeInfo = { + "ReferenceTime", + "Reference Time", + "The reference time relative to which the elapsed time is specified. The format " + "must be an ISO 8601-compliant date string" + }; + + constexpr openspace::properties::Property::PropertyInfo SimplifyTimeInfo = { + "SimplifyTime", + "Simplify Time", + "If this value is enabled, the elapsed time will be simplified into seconds, " + "minutes, hours, etc. If the value is disabled, the elapsed time is always " + "presented in seconds. The default value for this is 'true'." + }; + + constexpr openspace::properties::Property::PropertyInfo LowestTimeUnitInfo = { + "LowestTimeUnit", + "Lowest Time Unit when Simplifying", + "If 'SimplifyTime' is enabled, this is the lowest time unit that will be shown. " + "All finer grained timesteps will be ignored." + }; + + struct [[codegen::Dictionary(DashboardItemElapsedTime)]] Parameters { + // [[codegen::verbatim(FormatStringInfo.description)]] + std::optional formatString; + + // [[codegen::verbatim(ReferenceTimeInfo.description)]] + std::string referenceTime; + + // [[codegen::verbatim(SimplifyTimeInfo.description)]] + std::optional simplifyTime; + + enum class [[codegen::map(openspace::TimeUnit)]] TimeUnit { + Nanosecond, + Microsecond, + Millisecond, + Second, + Minute, + Hour, + Day, + Month, + Year + }; + + // [[codegen::verbatim(LowestTimeUnitInfo.description)]] + std::optional lowestTimeUnit; + }; +#include "dashboarditemelapsedtime_codegen.cpp" +} // namespace + +namespace openspace { + +documentation::Documentation DashboardItemElapsedTime::Documentation() { + return codegen::doc("base_dashboarditem_elapsedtime"); +} + +DashboardItemElapsedTime::DashboardItemElapsedTime(const ghoul::Dictionary& dictionary) + : DashboardTextItem(dictionary, 15.f) + , _formatString(FormatStringInfo, "Elapsed time: {}") + , _referenceTime(ReferenceTimeInfo) + , _simplifyTime(SimplifyTimeInfo, true) + , _lowestTimeUnit(LowestTimeUnitInfo) +{ + const Parameters p = codegen::bake(dictionary); + + _formatString = p.formatString.value_or(_formatString); + + _referenceTime.onChange([this]() { + _referenceJ2000 = Time::convertTime(_referenceTime); + }); + _referenceTime = p.referenceTime; + addProperty(_referenceTime); + + _simplifyTime = p.simplifyTime.value_or(_simplifyTime); + addProperty(_simplifyTime); + + for (TimeUnit u : TimeUnits) { + _lowestTimeUnit.addOption(static_cast(u), std::string(nameForTimeUnit(u))); + } + _lowestTimeUnit = static_cast(TimeUnit::Second); + TimeUnit u = codegen::map( + p.lowestTimeUnit.value_or(Parameters::TimeUnit::Second) + ); + _lowestTimeUnit = static_cast(u); + addProperty(_lowestTimeUnit); +} + +void DashboardItemElapsedTime::render(glm::vec2& penPosition) { + ZoneScoped + + double delta = global::timeManager->time().j2000Seconds() - _referenceJ2000; + + if (_simplifyTime) { + using namespace std::chrono; + + TimeUnit lowestTime = TimeUnit(_lowestTimeUnit.value()); + std::string_view lowestUnitS = nameForTimeUnit(lowestTime, false); + std::string_view lowestUnitP = nameForTimeUnit(lowestTime, true); + + std::vector> ts = splitTime(delta); + std::string time; + for (const std::pair& t : ts) { + time += fmt::format("{} {} ", t.first, t.second); + if (t.second == lowestUnitS || t.second == lowestUnitP) { + // We have reached the lowest unit the user was interested in + break; + } + } + + // Remove the " " at the end + time = time.substr(0, time.size() - 2); + + RenderFont( + *_font, + penPosition, + fmt::format(fmt::runtime(_formatString.value()), time) + ); + } + else { + std::string time = fmt::format("{} s", delta); + RenderFont( + *_font, + penPosition, + fmt::format(fmt::runtime(_formatString.value()), time) + ); + } + + penPosition.y -= _font->height(); +} + +glm::vec2 DashboardItemElapsedTime::size() const { + ZoneScoped + + const double delta = global::timeManager->time().j2000Seconds() - _referenceJ2000; + return _font->boundingBox(fmt::format(fmt::runtime(_formatString.value()), delta)); +} + +} // namespace openspace diff --git a/modules/base/dashboard/dashboarditemelapsedtime.h b/modules/base/dashboard/dashboarditemelapsedtime.h new file mode 100644 index 0000000000..83a5961b3c --- /dev/null +++ b/modules/base/dashboard/dashboarditemelapsedtime.h @@ -0,0 +1,59 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __OPENSPACE_MODULE_BASE___DASHBOARDITEMELAPSEDTIME___H__ +#define __OPENSPACE_MODULE_BASE___DASHBOARDITEMELAPSEDTIME___H__ + +#include + +#include +#include +#include + +namespace openspace { + +namespace documentation { struct Documentation; } + +class DashboardItemElapsedTime : public DashboardTextItem { +public: + DashboardItemElapsedTime(const ghoul::Dictionary& dictionary); + ~DashboardItemElapsedTime() override = default; + + void render(glm::vec2& penPosition) override; + + glm::vec2 size() const override; + + static documentation::Documentation Documentation(); + +private: + properties::StringProperty _formatString; + properties::StringProperty _referenceTime; + double _referenceJ2000 = 0.0; + properties::BoolProperty _simplifyTime; + properties::OptionProperty _lowestTimeUnit; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___DASHBOARDITEMDATE___H__ diff --git a/modules/imgui/src/guispacetimecomponent.cpp b/modules/imgui/src/guispacetimecomponent.cpp index 9b4c47f2cd..8de8461198 100644 --- a/modules/imgui/src/guispacetimecomponent.cpp +++ b/modules/imgui/src/guispacetimecomponent.cpp @@ -339,9 +339,9 @@ void GuiSpaceTimeComponent::render() { { const float dt = static_cast(global::timeManager->targetDeltaTime()); if (_firstFrame) { - const std::pair& dtInfo = simplifyTime(dt); + const std::pair& dtInfo = simplifyTime(dt); _deltaTime = static_cast(dtInfo.first); - _deltaTimeUnit = timeUnitFromString(dtInfo.second.c_str()); + _deltaTimeUnit = timeUnitFromString(dtInfo.second); _timeUnits = std::accumulate( openspace::TimeUnits.begin(), diff --git a/modules/spacecraftinstruments/dashboard/dashboarditeminstruments.cpp b/modules/spacecraftinstruments/dashboard/dashboarditeminstruments.cpp index 4ddd6accfc..ae6a0512e6 100644 --- a/modules/spacecraftinstruments/dashboard/dashboarditeminstruments.cpp +++ b/modules/spacecraftinstruments/dashboard/dashboarditeminstruments.cpp @@ -144,7 +144,7 @@ void DashboardItemInstruments::render(glm::vec2& penPosition) { ghoul::fontrendering::CrDirection::Down ); - std::pair remainingConv = simplifyTime(remaining); + std::pair remainingConv = simplifyTime(remaining); // If the remaining time is below 5 minutes, we switch over to seconds display if (remaining < 5 * 60) { diff --git a/src/util/timeconversion.cpp b/src/util/timeconversion.cpp index 7dedad8140..4d38a3d384 100644 --- a/src/util/timeconversion.cpp +++ b/src/util/timeconversion.cpp @@ -28,57 +28,91 @@ #include +namespace { + std::pair extractUnit(double seconds) { + using namespace openspace; + + const double secondsVal = glm::abs(seconds); + + if (secondsVal == 0.0) { + return { 0.0, TimeUnit::Second }; + } + else if (secondsVal <= 1e-6) { + const double val = seconds / 1e-9; + return { val, TimeUnit::Nanosecond }; + } + else if (secondsVal <= 1e-3) { + const double val = seconds / 1e-6; + return { val, TimeUnit::Microsecond }; + } + else if (secondsVal <= 1.0) { + const double val = seconds / 1e-3; + return { val, TimeUnit::Millisecond }; + } + else if (secondsVal <= SecondsPerMinute) { + return { seconds, TimeUnit::Second }; + } + else if (secondsVal <= SecondsPerHour) { + const double val = seconds / SecondsPerMinute; + return { val, TimeUnit::Minute }; + } + else if (secondsVal <= SecondsPerDay) { + const double val = seconds / SecondsPerHour; + return { val, TimeUnit::Hour }; + } + else if (secondsVal <= SecondsPerMonth) { + const double val = seconds / SecondsPerDay; + return { val, TimeUnit::Day }; + } + else if (secondsVal <= SecondsPerYear) { + const double val = seconds / SecondsPerMonth; + return { val, TimeUnit::Month }; + } + else { + const double val = seconds / SecondsPerYear; + return { val, TimeUnit::Year }; + } + } +} // namespace + namespace openspace { -std::pair simplifyTime(double seconds, bool forceSingularForm) { - const double secondsVal = glm::abs(seconds); +std::pair simplifyTime(double seconds, bool forceSingularForm) { + std::pair p = extractUnit(seconds); - if (secondsVal == 0.0) { - return { 0.0, forceSingularForm ? "second" : "seconds" }; - } - else if (secondsVal > 1e-3 && secondsVal < SecondsPerMinute) { - return { seconds, (seconds == 1.0 || forceSingularForm) ? "second" : "seconds" }; - } + bool pluralForm = (p.first != 1.0 && !forceSingularForm); + return { p.first, nameForTimeUnit(p.second, pluralForm) }; +} - if (secondsVal <= 1e-9) { - const double val = seconds / 1e-9; - return { val, (val == 1.0 || forceSingularForm) ? "nanosecond" : "nanoseconds" }; - } - else if (secondsVal <= 1e-6) { - const double val = seconds / 1e-6; - return { - val, - (val == 1.0 || forceSingularForm) ? "microsecond" : "microseconds" - }; - } - else if (secondsVal <= 1e-3) { - const double val = seconds / 1e-3; - return { - val, - (val == 1.0 || forceSingularForm) ? "millisecond" : "milliseconds" - }; - } +std::vector> splitTime(double seconds, + bool forceSingularForm) +{ + std::vector> res; - if (secondsVal >= SecondsPerYear) { - const double val = seconds / SecondsPerYear; - return { val, (val == 1.0 || forceSingularForm) ? "year" : "years" }; - } - else if (secondsVal >= SecondsPerMonth) { - const double val = seconds / SecondsPerMonth; - return { val, (val == 1.0 || forceSingularForm) ? "month" : "months" }; - } - else if (secondsVal >= SecondsPerDay) { - const double val = seconds / SecondsPerDay; - return { val, (val == 1.0 || forceSingularForm) ? "day" : "days" }; - } - else if (secondsVal >= SecondsPerHour) { - const double val = seconds / SecondsPerHour; - return { val, (val == 1.0 || forceSingularForm) ? "hour" : "hours" }; - } - else { - const double val = seconds / SecondsPerMinute; - return { val, (val == 1.0 || forceSingularForm) ? "minute" : "minutes" }; - } + double secondsVal = std::abs(seconds); + + do { + std::pair p = extractUnit(secondsVal); + + if (p.second == TimeUnit::Nanosecond) { + // We have reached the lowest supported time unit + + bool pluralForm = (p.first != 1.0 && !forceSingularForm); + res.push_back({ p.first, nameForTimeUnit(p.second, pluralForm) }); + break; + } + + double pt = std::floor(p.first); + + // Add the unit the list + bool pluralForm = (p.first != 1.0 && !forceSingularForm); + res.push_back({ pt, nameForTimeUnit(p.second, pluralForm) }); + + // Adjust the remaining time + secondsVal -= convertTime(pt, p.second, TimeUnit::Second); + } while (secondsVal != 0.0); + + return res; } } // namespace openspace diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 93a213a112..916e8bede9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,8 +40,9 @@ add_executable( test_rawvolumeio.cpp test_scriptscheduler.cpp test_spicemanager.cpp - test_timequantizer.cpp + test_timeconversion.cpp test_timeline.cpp + test_timequantizer.cpp property/test_property_optionproperty.cpp property/test_property_listproperties.cpp diff --git a/tests/test_timeconversion.cpp b/tests/test_timeconversion.cpp new file mode 100644 index 0000000000..77f02be295 --- /dev/null +++ b/tests/test_timeconversion.cpp @@ -0,0 +1,567 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2022 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#include "catch2/catch.hpp" + +#include + +using namespace openspace; + +TEST_CASE("TimeConversion: Simplify Time Round", "[timeconversion]") { + { + std::pair p = simplifyTime(0.0, false); + CHECK(p.first == 0.0); + CHECK(p.second == "seconds"); + } + { + std::pair p = simplifyTime(0.0, true); + CHECK(p.first == 0.0); + CHECK(p.second == "second"); + } + { + std::pair p = simplifyTime(2e-9, false); + CHECK(p.first == 2.0); + CHECK(p.second == "nanoseconds"); + } + { + std::pair p = simplifyTime(2e-9, true); + CHECK(p.first == 2.0); + CHECK(p.second == "nanosecond"); + } + { + std::pair p = simplifyTime(2e-6, false); + CHECK(p.first == 2.0); + CHECK(p.second == "microseconds"); + } + { + std::pair p = simplifyTime(2e-6, true); + CHECK(p.first == 2.0); + CHECK(p.second == "microsecond"); + } + { + std::pair p = simplifyTime(2e-3, false); + CHECK(p.first == 2.0); + CHECK(p.second == "milliseconds"); + } + { + std::pair p = simplifyTime(2e-3, true); + CHECK(p.first == 2.0); + CHECK(p.second == "millisecond"); + } + { + std::pair p = simplifyTime(2.0, false); + CHECK(p.first == 2.0); + CHECK(p.second == "seconds"); + } + { + std::pair p = simplifyTime(2.0, true); + CHECK(p.first == 2.0); + CHECK(p.second == "second"); + } + { + std::pair p = simplifyTime(120.0, false); + CHECK(p.first == 2.0); + CHECK(p.second == "minutes"); + } + { + std::pair p = simplifyTime(120.0, true); + CHECK(p.first == 2.0); + CHECK(p.second == "minute"); + } + { + std::pair p = simplifyTime(7200.0, false); + CHECK(p.first == 2.0); + CHECK(p.second == "hours"); + } + { + std::pair p = simplifyTime(7200.0, true); + CHECK(p.first == 2.0); + CHECK(p.second == "hour"); + } + { + std::pair p = simplifyTime(172800.0, false); + CHECK(p.first == 2.0); + CHECK(p.second == "days"); + } + { + std::pair p = simplifyTime(172800.0, true); + CHECK(p.first == 2.0); + CHECK(p.second == "day"); + } + { + std::pair p = simplifyTime(5259492.0, false); + CHECK(p.first == 2.0); + CHECK(p.second == "months"); + } + { + std::pair p = simplifyTime(5259492.0, true); + CHECK(p.first == 2.0); + CHECK(p.second == "month"); + } + { + std::pair p = simplifyTime(63113904.0, false); + CHECK(p.first == 2.0); + CHECK(p.second == "years"); + } + { + std::pair p = simplifyTime(63113904.0, true); + CHECK(p.first == 2.0); + CHECK(p.second == "year"); + } +} + +TEST_CASE("TimeConversion: Simplify Time Fractional", "[timeconversion]") { + { + std::pair p = simplifyTime(32e-10, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "nanoseconds"); + } + { + std::pair p = simplifyTime(32e-10, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "nanosecond"); + } + { + std::pair p = simplifyTime(32e-7, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "microseconds"); + } + { + std::pair p = simplifyTime(32e-7, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "microsecond"); + } + { + std::pair p = simplifyTime(32e-4, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "milliseconds"); + } + { + std::pair p = simplifyTime(32e-4, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "millisecond"); + } + { + std::pair p = simplifyTime(3.2, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "seconds"); + } + { + std::pair p = simplifyTime(3.2, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "second"); + } + { + std::pair p = simplifyTime(192.0, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "minutes"); + } + { + std::pair p = simplifyTime(192.0, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "minute"); + } + { + std::pair p = simplifyTime(11520.0, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "hours"); + } + { + std::pair p = simplifyTime(11520.0, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "hour"); + } + { + std::pair p = simplifyTime(276480.0, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "days"); + } + { + std::pair p = simplifyTime(276480.0, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "day"); + } + { + std::pair p = simplifyTime(8415187.2, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "months"); + } + { + std::pair p = simplifyTime(8415187.2, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "month"); + } + { + std::pair p = simplifyTime(100982246.4, false); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "years"); + } + { + std::pair p = simplifyTime(100982246.4, true); + CHECK(Approx(p.first) == 3.2); + CHECK(p.second == "year"); + } +} + +TEST_CASE("TimeConversion: Split Time Multiple Round", "[timeconversion]") { + { + std::vector> p = splitTime(0.0, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 0.0); + CHECK(p[0].second == "seconds"); + } + { + std::vector> p = splitTime(0.0, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 0.0); + CHECK(p[0].second == "second"); + } + { + std::vector> p = splitTime(2e-9, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "nanoseconds"); + } + { + std::vector> p = splitTime(2e-9, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "nanosecond"); + } + { + std::vector> p = splitTime(2e-6, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "microseconds"); + } + { + std::vector> p = splitTime(2e-6, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "microsecond"); + } + { + std::vector> p = splitTime(2e-3, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "milliseconds"); + } + { + std::vector> p = splitTime(2e-3, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "millisecond"); + } + { + std::vector> p = splitTime(2.0, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "seconds"); + } + { + std::vector> p = splitTime(2.0, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "second"); + } + { + std::vector> p = splitTime(120.0, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "minutes"); + } + { + std::vector> p = splitTime(120.0, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "minute"); + } + { + std::vector> p = splitTime(7200.0, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "hours"); + } + { + std::vector> p = splitTime(7200.0, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "hour"); + } + { + std::vector> p = splitTime(172800.0, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "days"); + } + { + std::vector> p = splitTime(172800.0, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "day"); + } + { + std::vector> p = splitTime(5259492.0, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "months"); + } + { + std::vector> p = splitTime(5259492.0, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "month"); + } + { + std::vector> p = splitTime(63113904.0, false); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "years"); + } + { + std::vector> p = splitTime(63113904.0, true); + REQUIRE(p.size() == 1); + CHECK(p[0].first == 2.0); + CHECK(p[0].second == "year"); + } +} + +TEST_CASE("TimeConversion: Split Time Fractional", "[timeconversion]") { + { + std::vector> p = splitTime(32e-10, false); + REQUIRE(p.size() == 1); + CHECK(Approx(p[0].first) == 3.2); + CHECK(p[0].second == "nanoseconds"); + } + { + std::vector> p = splitTime(32e-10, true); + REQUIRE(p.size() == 1); + CHECK(Approx(p[0].first) == 3.2); + CHECK(p[0].second == "nanosecond"); + } + { + std::vector> p = splitTime(32e-7, false); + REQUIRE(p.size() == 2); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "microseconds"); + CHECK(Approx(p[1].first) == 200.0); + CHECK(p[1].second == "nanoseconds"); + } + { + std::vector> p = splitTime(32e-7, true); + REQUIRE(p.size() == 2); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "microsecond"); + CHECK(Approx(p[1].first) == 200.0); + CHECK(p[1].second == "nanosecond"); + } + { + std::vector> p = splitTime(32e-4, false); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "milliseconds"); + CHECK(Approx(p[1].first) == 200.0); + CHECK(p[1].second == "microseconds"); + // This is some floating point inaccuracy + CHECK(p[2].first < 1e-3); + CHECK(p[2].second == "nanoseconds"); + } + { + std::vector> p = splitTime(32e-4, true); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "millisecond"); + CHECK(Approx(p[1].first) == 200.0); + CHECK(p[1].second == "microsecond"); + // This is some floating point inaccuracy + CHECK(p[2].first < 1e-3); + CHECK(p[2].second == "nanosecond"); + } + { + std::vector> p = splitTime(3.2, false); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "seconds"); + CHECK(Approx(p[1].first) == 200.0); + CHECK(p[1].second == "milliseconds"); + // This is some floating point inaccuracy + CHECK(p[2].first < 1e-3); + CHECK(p[2].second == "nanoseconds"); + } + { + std::vector> p = splitTime(3.2, true); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "second"); + CHECK(Approx(p[1].first) == 200.0); + CHECK(p[1].second == "millisecond"); + // This is some floating point inaccuracy + CHECK(p[2].first < 1e-3); + CHECK(p[2].second == "nanosecond"); + } + { + std::vector> p = splitTime(192.0, false); + REQUIRE(p.size() == 2); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "minutes"); + CHECK(Approx(p[1].first) == 12.0); + CHECK(p[1].second == "seconds"); + } + { + std::vector> p = splitTime(192.0, true); + REQUIRE(p.size() == 2); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "minute"); + CHECK(Approx(p[1].first) == 12.0); + CHECK(p[1].second == "second"); + } + { + std::vector> p = splitTime(11520.0, false); + REQUIRE(p.size() == 2); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "hours"); + CHECK(Approx(p[1].first) == 12.0); + CHECK(p[1].second == "minutes"); + } + { + std::vector> p = splitTime(11520.0, true); + REQUIRE(p.size() == 2); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "hour"); + CHECK(Approx(p[1].first) == 12.0); + CHECK(p[1].second == "minute"); + } + { + std::vector> p = splitTime(276480.0, false); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "days"); + CHECK(Approx(p[1].first) == 4); + CHECK(p[1].second == "hours"); + CHECK(Approx(p[2].first) == 48); + CHECK(p[2].second == "minutes"); + } + { + std::vector> p = splitTime(276480.0, true); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "day"); + CHECK(Approx(p[1].first) == 4); + CHECK(p[1].second == "hour"); + CHECK(Approx(p[2].first) == 48); + CHECK(p[2].second == "minute"); + } + { + std::vector> p = splitTime(8414838.0, false); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "months"); + CHECK(Approx(p[1].first) == 6.0); + CHECK(p[1].second == "days"); + CHECK(Approx(p[2].first) == 2.0); + CHECK(p[2].second == "hours"); + + } + { + std::vector> p = splitTime(8414838.0, true); + REQUIRE(p.size() == 3); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "month"); + CHECK(Approx(p[1].first) == 6.0); + CHECK(p[1].second == "day"); + CHECK(Approx(p[2].first) == 2.0); + CHECK(p[2].second == "hour"); + } + { + std::vector> p = + splitTime(100981548.0, false); + REQUIRE(p.size() == 4); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "years"); + CHECK(Approx(p[1].first) == 2.0); + CHECK(p[1].second == "months"); + CHECK(Approx(p[2].first) == 12.0); + CHECK(p[2].second == "days"); + CHECK(Approx(p[3].first) == 4.0); + CHECK(p[3].second == "hours"); + } + { + std::vector> p = splitTime(100981548.0, true); + REQUIRE(p.size() == 4); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "year"); + CHECK(Approx(p[1].first) == 2.0); + CHECK(p[1].second == "month"); + CHECK(Approx(p[2].first) == 12.0); + CHECK(p[2].second == "day"); + CHECK(Approx(p[3].first) == 4.0); + CHECK(p[3].second == "hour"); + } + { + std::vector> p = + splitTime(100981676.388, false); + REQUIRE(p.size() == 9); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "years"); + CHECK(Approx(p[1].first) == 2.0); + CHECK(p[1].second == "months"); + CHECK(Approx(p[2].first) == 12.0); + CHECK(p[2].second == "days"); + CHECK(Approx(p[3].first) == 4.0); + CHECK(p[3].second == "hours"); + CHECK(Approx(p[4].first) == 2.0); + CHECK(p[4].second == "minutes"); + CHECK(Approx(p[5].first) == 8.0); + CHECK(p[5].second == "seconds"); + CHECK(Approx(p[6].first) == 387.0); + CHECK(p[6].second == "milliseconds"); + CHECK(Approx(p[7].first) == 999.0); + CHECK(p[7].second == "microseconds"); + CHECK(Approx(p[8].first) == 996.54293059); + CHECK(p[8].second == "nanoseconds"); + } + { + std::vector> p = + splitTime(100981676.388, true); + REQUIRE(p.size() == 9); + CHECK(Approx(p[0].first) == 3.0); + CHECK(p[0].second == "year"); + CHECK(Approx(p[1].first) == 2.0); + CHECK(p[1].second == "month"); + CHECK(Approx(p[2].first) == 12.0); + CHECK(p[2].second == "day"); + CHECK(Approx(p[3].first) == 4.0); + CHECK(p[3].second == "hour"); + CHECK(Approx(p[4].first) == 2.0); + CHECK(p[4].second == "minute"); + CHECK(Approx(p[5].first) == 8.0); + CHECK(p[5].second == "second"); + CHECK(Approx(p[6].first) == 387.0); + CHECK(p[6].second == "millisecond"); + CHECK(Approx(p[7].first) == 999.0); + CHECK(p[7].second == "microsecond"); + CHECK(Approx(p[8].first) == 996.54293059); + CHECK(p[8].second == "nanosecond"); + } +}