diff --git a/include/openspace/util/spicemanager.h b/include/openspace/util/spicemanager.h index 25e9db0e0c..916dd64f2b 100644 --- a/include/openspace/util/spicemanager.h +++ b/include/openspace/util/spicemanager.h @@ -544,6 +544,8 @@ public: } } + std::string dateFromEphemerisTime(double ephemerisTime, const char* format); + /** * Returns the \p position of a \p target body relative to an \p observer in a * specific \p referenceFrame, optionally corrected for \p lightTime (planetary diff --git a/modules/base/CMakeLists.txt b/modules/base/CMakeLists.txt index 386e50515f..0a2c4b4d53 100644 --- a/modules/base/CMakeLists.txt +++ b/modules/base/CMakeLists.txt @@ -34,6 +34,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditempropertyvalue.h ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemsimulationincrement.h ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemspacing.h + ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemtext.h ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemvelocity.h ${CMAKE_CURRENT_SOURCE_DIR}/lightsource/cameralightsource.h ${CMAKE_CURRENT_SOURCE_DIR}/lightsource/scenegraphlightsource.h @@ -86,6 +87,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditempropertyvalue.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemsimulationincrement.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemspacing.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemtext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dashboard/dashboarditemvelocity.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lightsource/cameralightsource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lightsource/scenegraphlightsource.cpp diff --git a/modules/base/basemodule.cpp b/modules/base/basemodule.cpp index 830d528117..fca97888c9 100644 --- a/modules/base/basemodule.cpp +++ b/modules/base/basemodule.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,7 @@ void BaseModule::internalInitialize(const ghoul::Dictionary&) { "DashboardItemSimulationIncrement" ); fDashboard->registerClass("DashboardItemSpacing"); + fDashboard->registerClass("DashboardItemText"); fDashboard->registerClass("DashboardItemVelocity"); auto fRenderable = FactoryManager::ref().factory(); diff --git a/modules/base/dashboard/dashboarditemdate.cpp b/modules/base/dashboard/dashboarditemdate.cpp index 34b0dac408..cdd8e7ba52 100644 --- a/modules/base/dashboard/dashboarditemdate.cpp +++ b/modules/base/dashboard/dashboarditemdate.cpp @@ -27,10 +27,12 @@ #include #include #include +#include #include #include #include #include +#include #include namespace { @@ -49,6 +51,22 @@ namespace { "Font Size", "This value determines the size of the font that is used to render the date." }; + + constexpr openspace::properties::Property::PropertyInfo FormatStringInfo = { + "FormatString", + "Format String", + "The format text describing how this dashboard item renders it's text. This text " + "must contain exactly one {} which is a placeholder that will contain the date" + }; + + constexpr openspace::properties::Property::PropertyInfo TimeFormatInfo = { + "TimeFormat", + "Time Format", + "The format string used for formatting the date/time before being passed to the " + "string in FormatString. See " + "https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/timout_c.html for full " + "information about how to structure this format" + }; } // namespace namespace openspace { @@ -75,6 +93,18 @@ documentation::Documentation DashboardItemDate::Documentation() { new IntVerifier, Optional::Yes, FontSizeInfo.description + }, + { + FormatStringInfo.identifier, + new StringVerifier, + Optional::Yes, + FormatStringInfo.description + }, + { + TimeFormatInfo.identifier, + new StringVerifier, + Optional::Yes, + TimeFormatInfo.description } } }; @@ -84,6 +114,8 @@ DashboardItemDate::DashboardItemDate(const ghoul::Dictionary& dictionary) : DashboardItem(dictionary) , _fontName(FontNameInfo, KeyFontMono) , _fontSize(FontSizeInfo, DefaultFontSize, 6.f, 144.f, 1.f) + , _formatString(FormatStringInfo, "Date: {} UTC") + , _timeFormat(TimeFormatInfo, "YYYY MON DDTHR:MN:SC.### ::RND") { documentation::testSpecificationAndThrow( Documentation(), @@ -106,6 +138,16 @@ DashboardItemDate::DashboardItemDate(const ghoul::Dictionary& dictionary) _font = global::fontManager->font(_fontName, _fontSize); }); addProperty(_fontSize); + + if (dictionary.hasKey(FormatStringInfo.identifier)) { + _formatString = dictionary.value(FormatStringInfo.identifier); + } + addProperty(_formatString); + + if (dictionary.hasKey(TimeFormatInfo.identifier)) { + _timeFormat = dictionary.value(TimeFormatInfo.identifier); + } + addProperty(_timeFormat); _font = global::fontManager->font(_fontName, _fontSize); } @@ -113,20 +155,24 @@ DashboardItemDate::DashboardItemDate(const ghoul::Dictionary& dictionary) void DashboardItemDate::render(glm::vec2& penPosition) { ZoneScoped - RenderFont( - *_font, - penPosition, - fmt::format("Date: {} UTC", global::timeManager->time().UTC()) + std::string time = SpiceManager::ref().dateFromEphemerisTime( + global::timeManager->time().j2000Seconds(), + _timeFormat.value().c_str() ); + try { + RenderFont(*_font, penPosition, fmt::format(_formatString.value().c_str(), time)); + } + catch (const fmt::format_error&) { + LERRORC("DashboardItemDate", "Illegal format string"); + } penPosition.y -= _font->height(); } glm::vec2 DashboardItemDate::size() const { ZoneScoped - return _font->boundingBox( - fmt::format("Date: {} UTC", global::timeManager->time().UTC()) - ); + std::string_view time = global::timeManager->time().UTC(); + return _font->boundingBox(fmt::format(_formatString.value().c_str(), time)); } } // namespace openspace diff --git a/modules/base/dashboard/dashboarditemdate.h b/modules/base/dashboard/dashboarditemdate.h index 139b03477a..81e7c754e1 100644 --- a/modules/base/dashboard/dashboarditemdate.h +++ b/modules/base/dashboard/dashboarditemdate.h @@ -50,6 +50,8 @@ public: private: properties::StringProperty _fontName; properties::FloatProperty _fontSize; + properties::StringProperty _formatString; + properties::StringProperty _timeFormat; std::shared_ptr _font; }; diff --git a/modules/base/dashboard/dashboarditemdistance.cpp b/modules/base/dashboard/dashboarditemdistance.cpp index e8f83a77bd..1c36425646 100644 --- a/modules/base/dashboard/dashboarditemdistance.cpp +++ b/modules/base/dashboard/dashboarditemdistance.cpp @@ -101,6 +101,14 @@ namespace { "to convert the meters into." }; + constexpr openspace::properties::Property::PropertyInfo FormatStringInfo = { + "FormatString", + "Format String", + "The format string that is used for formatting the distance string. This format " + "receives four parameters: The name of the source, the name of the destination " + "the value of the distance and the unit of the distance" + }; + std::vector unitList() { std::vector res(openspace::DistanceUnits.size()); std::transform( @@ -179,6 +187,12 @@ documentation::Documentation DashboardItemDistance::Documentation() { new StringInListVerifier(unitList()), Optional::Yes, RequestedUnitInfo.description + }, + { + FormatStringInfo.identifier, + new StringVerifier, + Optional::Yes, + FormatStringInfo.description } } }; @@ -206,6 +220,7 @@ DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary properties::StringProperty(DestinationNodeNameInfo), nullptr } + , _formatString(FormatStringInfo, "Distance from {} to {}: {:f} {}") { documentation::testSpecificationAndThrow( Documentation(), @@ -358,6 +373,11 @@ DashboardItemDistance::DashboardItemDistance(const ghoul::Dictionary& dictionary _requestedUnit.setVisibility(properties::Property::Visibility::Hidden); addProperty(_requestedUnit); + if (dictionary.hasKey(FormatStringInfo.identifier)) { + _formatString = dictionary.value(FormatStringInfo.identifier); + } + addProperty(_formatString); + _font = global::fontManager->font(_fontName, _fontSize); _buffer.resize(256); @@ -445,14 +465,19 @@ void DashboardItemDistance::render(glm::vec2& penPosition) { } std::fill(_buffer.begin(), _buffer.end(), 0); - char* end = fmt::format_to( - _buffer.data(), - "Distance from {} to {}: {:f} {}\0", - sourceInfo.second, destinationInfo.second, dist.first, dist.second - ); + try { + char* end = fmt::format_to( + _buffer.data(), + _formatString.value().c_str(), + sourceInfo.second, destinationInfo.second, dist.first, dist.second + ); - std::string_view text = std::string_view(_buffer.data(), end - _buffer.data()); - RenderFont(*_font, penPosition, text); + std::string_view text = std::string_view(_buffer.data(), end - _buffer.data()); + RenderFont(*_font, penPosition, text); + } + catch (const fmt::format_error&) { + LERRORC("DashboardItemDate", "Illegal format string"); + } penPosition.y -= _font->height(); } diff --git a/modules/base/dashboard/dashboarditemdistance.h b/modules/base/dashboard/dashboarditemdistance.h index f66cd745c4..8ac5d5f926 100644 --- a/modules/base/dashboard/dashboarditemdistance.h +++ b/modules/base/dashboard/dashboarditemdistance.h @@ -75,6 +75,7 @@ private: properties::FloatProperty _fontSize; properties::BoolProperty _doSimplification; properties::OptionProperty _requestedUnit; + properties::StringProperty _formatString; Component _source; Component _destination; diff --git a/modules/base/dashboard/dashboarditemsimulationincrement.cpp b/modules/base/dashboard/dashboarditemsimulationincrement.cpp index 503f178d98..0bf0decd45 100644 --- a/modules/base/dashboard/dashboarditemsimulationincrement.cpp +++ b/modules/base/dashboard/dashboarditemsimulationincrement.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include namespace { @@ -66,6 +67,26 @@ namespace { "convert the seconds into." }; + constexpr openspace::properties::Property::PropertyInfo TransitionFormatInfo = { + "TransitionFormat", + "Transition Format", + "Format string used to format the text used while in a delta time transition, " + "that is if the current delta time is being interpolated to reach a target " + "delta time. This format gets five parameters in this order: The target delta " + "time value, the target delta time unit, the string 'Paused' if the delta time " + "is paused or the empty string otherwise, the current delta time value, and the " + "current delta time unit" + }; + + constexpr openspace::properties::Property::PropertyInfo RegularFormatInfo = { + "RegularFormat", + "Regular Format", + "The format string used to format the text if the target delta time is the same " + "as the current delta time. This format gets three parameters in this order: " + "The target delta value, the target delta unit, and the string 'Paused' if the " + "delta time is paused or the empty string otherwise" + }; + std::vector unitList() { std::vector res(openspace::TimeUnits.size()); std::transform( @@ -114,6 +135,18 @@ documentation::Documentation DashboardItemSimulationIncrement::Documentation() { new StringInListVerifier(unitList()), Optional::Yes, RequestedUnitInfo.description + }, + { + TransitionFormatInfo.identifier, + new StringVerifier, + Optional::Yes, + TransitionFormatInfo.description + }, + { + RegularFormatInfo.identifier, + new StringVerifier, + Optional::Yes, + RegularFormatInfo.description } } }; @@ -126,6 +159,11 @@ DashboardItemSimulationIncrement::DashboardItemSimulationIncrement( , _fontSize(FontSizeInfo, DefaultFontSize, 6.f, 144.f, 1.f) , _doSimplification(SimplificationInfo, true) , _requestedUnit(RequestedUnitInfo, properties::OptionProperty::DisplayType::Dropdown) + , _transitionFormat( + TransitionFormatInfo, + "Simulation increment: {:.1f} {:s} / second{:s} (current: {:.1f} {:s})" + ) + , _regularFormat(RegularFormatInfo, "Simulation increment: {:.1f} {:s} / second{:s}") { documentation::testSpecificationAndThrow( Documentation(), @@ -173,6 +211,19 @@ DashboardItemSimulationIncrement::DashboardItemSimulationIncrement( _requestedUnit.setVisibility(properties::Property::Visibility::Hidden); addProperty(_requestedUnit); + if (dictionary.hasKey(TransitionFormatInfo.identifier)) { + _transitionFormat = dictionary.value( + TransitionFormatInfo.identifier + ); + } + addProperty(_transitionFormat); + + if (dictionary.hasKey(RegularFormatInfo.identifier)) { + _regularFormat = dictionary.value(RegularFormatInfo.identifier); + } + addProperty(_regularFormat); + + _font = global::fontManager->font(_fontName, _fontSize); } @@ -203,28 +254,33 @@ void DashboardItemSimulationIncrement::render(glm::vec2& penPosition) { std::string pauseText = global::timeManager->isPaused() ? " (Paused)" : ""; - if (targetDt != currentDt && !global::timeManager->isPaused()) { - // We are in the middle of a transition - RenderFont( - *_font, - penPosition, - fmt::format( - "Simulation increment: {:.1f} {:s} / second{:s} (current: {:.1f} {:s})", - targetDeltaTime.first, targetDeltaTime.second, - pauseText, - currentDeltaTime.first, currentDeltaTime.second - ) - ); + try { + if (targetDt != currentDt && !global::timeManager->isPaused()) { + // We are in the middle of a transition + RenderFont( + *_font, + penPosition, + fmt::format( + _transitionFormat.value().c_str(), + targetDeltaTime.first, targetDeltaTime.second, + pauseText, + currentDeltaTime.first, currentDeltaTime.second + ) + ); + } + else { + RenderFont( + *_font, + penPosition, + fmt::format( + _regularFormat.value().c_str(), + targetDeltaTime.first, targetDeltaTime.second, pauseText + ) + ); + } } - else { - RenderFont( - *_font, - penPosition, - fmt::format( - "Simulation increment: {:.1f} {:s} / second{:s}", - targetDeltaTime.first, targetDeltaTime.second, pauseText - ) - ); + catch (const fmt::format_error&) { + LERRORC("DashboardItemDate", "Illegal format string"); } penPosition.y -= _font->height(); } diff --git a/modules/base/dashboard/dashboarditemsimulationincrement.h b/modules/base/dashboard/dashboarditemsimulationincrement.h index 389778f0c2..3643856205 100644 --- a/modules/base/dashboard/dashboarditemsimulationincrement.h +++ b/modules/base/dashboard/dashboarditemsimulationincrement.h @@ -54,6 +54,9 @@ private: properties::BoolProperty _doSimplification; properties::OptionProperty _requestedUnit; + properties::StringProperty _transitionFormat; + properties::StringProperty _regularFormat; + std::shared_ptr _font; }; diff --git a/modules/base/dashboard/dashboarditemtext.cpp b/modules/base/dashboard/dashboarditemtext.cpp new file mode 100644 index 0000000000..2423aec13e --- /dev/null +++ b/modules/base/dashboard/dashboarditemtext.cpp @@ -0,0 +1,143 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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 + +namespace { + constexpr const char* KeyFontMono = "Mono"; + constexpr const float DefaultFontSize = 10.f; + + constexpr openspace::properties::Property::PropertyInfo FontNameInfo = { + "FontName", + "Font Name", + "This value is the name of the font that is used. It can either refer to an " + "internal name registered previously, or it can refer to a path that is used." + }; + + constexpr openspace::properties::Property::PropertyInfo FontSizeInfo = { + "FontSize", + "Font Size", + "This value determines the size of the font that is used to render the date." + }; + + constexpr openspace::properties::Property::PropertyInfo TextInfo = { + "Text", + "Text", + "The text to be displayed" + }; +} // namespace + +namespace openspace { + +documentation::Documentation DashboardItemText::Documentation() { + using namespace documentation; + return { + "DashboardItem Text", + "base_dashboarditem_text", + { + { + "Type", + new StringEqualVerifier("DashboardItemText"), + Optional::No + }, + { + FontNameInfo.identifier, + new StringVerifier, + Optional::Yes, + FontNameInfo.description + }, + { + FontSizeInfo.identifier, + new IntVerifier, + Optional::Yes, + FontSizeInfo.description + }, + { + TextInfo.identifier, + new StringVerifier, + Optional::Yes, + TextInfo.description + } + } + }; +} + +DashboardItemText::DashboardItemText(const ghoul::Dictionary& dictionary) + : DashboardItem(dictionary) + , _fontName(FontNameInfo, KeyFontMono) + , _fontSize(FontSizeInfo, DefaultFontSize, 6.f, 144.f, 1.f) + , _text(TextInfo, "") +{ + documentation::testSpecificationAndThrow( + Documentation(), + dictionary, + "DashboardItemText" + ); + + if (dictionary.hasKey(FontNameInfo.identifier)) { + _fontName = dictionary.value(FontNameInfo.identifier); + } + _fontName.onChange([this]() { + _font = global::fontManager->font(_fontName, _fontSize); + }); + addProperty(_fontName); + + if (dictionary.hasKey(FontSizeInfo.identifier)) { + _fontSize = static_cast(dictionary.value(FontSizeInfo.identifier)); + } + _fontSize.onChange([this]() { + _font = global::fontManager->font(_fontName, _fontSize); + }); + addProperty(_fontSize); + + if (dictionary.hasKey(TextInfo.identifier)) { + _text = dictionary.value(TextInfo.identifier); + }; + addProperty(_text); + + _font = global::fontManager->font(_fontName, _fontSize); +} + +void DashboardItemText::render(glm::vec2& penPosition) { + ZoneScoped + + RenderFont(*_font, penPosition, _text.value()); + penPosition.y -= _font->height(); +} + +glm::vec2 DashboardItemText::size() const { + ZoneScoped + + return _font->boundingBox(_text.value()); +} + +} // namespace openspace diff --git a/modules/base/dashboard/dashboarditemtext.h b/modules/base/dashboard/dashboarditemtext.h new file mode 100644 index 0000000000..8215b52e7d --- /dev/null +++ b/modules/base/dashboard/dashboarditemtext.h @@ -0,0 +1,60 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2020 * + * * + * 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___DASHBOARDITEMTEXT___H__ +#define __OPENSPACE_MODULE_BASE___DASHBOARDITEMTEXT___H__ + +#include + +#include +#include + +namespace ghoul::fontrendering { class Font; } + +namespace openspace { + +namespace documentation { struct Documentation; } + +class DashboardItemText : public DashboardItem { +public: + DashboardItemText(const ghoul::Dictionary& dictionary); + virtual ~DashboardItemText() = default; + + void render(glm::vec2& penPosition) override; + + glm::vec2 size() const override; + + static documentation::Documentation Documentation(); + +private: + properties::StringProperty _fontName; + properties::FloatProperty _fontSize; + properties::StringProperty _text; + + std::shared_ptr _font; +}; + +} // namespace openspace + +#endif // __OPENSPACE_MODULE_BASE___DASHBOARDITEMTEXT___H__ diff --git a/src/util/spicemanager.cpp b/src/util/spicemanager.cpp index 0abe2cabc3..6b1c86146b 100644 --- a/src/util/spicemanager.cpp +++ b/src/util/spicemanager.cpp @@ -571,6 +571,22 @@ double SpiceManager::ephemerisTimeFromDate(const char* timeString) const { return et; } +std::string SpiceManager::dateFromEphemerisTime(double ephemerisTime, const char* format) +{ + char Buffer[128]; + std::memset(Buffer, char(0), 128); + + timout_c(ephemerisTime, format, 128, Buffer); + if (failed_c()) { + throwSpiceError(fmt::format( + "Error converting ephemeris time '{}' to date with format '{}'", + ephemerisTime, format + )); + } + + return std::string(Buffer); +} + glm::dvec3 SpiceManager::targetPosition(const std::string& target, const std::string& observer, const std::string& referenceFrame, diff --git a/src/util/time.cpp b/src/util/time.cpp index a448438b19..117e487f84 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -97,8 +97,7 @@ std::string_view Time::UTC() const { std::memset(b, 0, 32); SpiceManager::ref().dateFromEphemerisTime(_time, b, 32, Format); - - return std::string_view(b, 32); + return std::string_view(b); } std::string_view Time::ISO8601() const {