diff --git a/data/assets/newhorizons.scene b/data/assets/newhorizons.scene index 7ea633324d..af40f70ee0 100644 --- a/data/assets/newhorizons.scene +++ b/data/assets/newhorizons.scene @@ -101,7 +101,7 @@ local Keybindings = { }, { Key = "l", - Command = propertyHelper.invert('Scene.Labels.renderable.Enabled'), + Command = propertyHelper.fadeInOut('Scene.Labels.renderable.Opacity', 2.0), Documentation = "Toggles the visibility of the labels for the New Horizons instruments.", Local = false }, diff --git a/data/assets/scene/solarsystem/planets/earth/moon/moon.asset b/data/assets/scene/solarsystem/planets/earth/moon/moon.asset index afda079c73..44692f8ca3 100644 --- a/data/assets/scene/solarsystem/planets/earth/moon/moon.asset +++ b/data/assets/scene/solarsystem/planets/earth/moon/moon.asset @@ -33,6 +33,7 @@ local Moon = { -- Utah based servers { Identifier = "ClemUvvis", + Name = "Clem Uvvis", FilePath = mapServiceConfigs .. "/Utah/ClemUvvis.wms" }, { @@ -55,6 +56,7 @@ local Moon = { -- Utah based servers { Identifier = "LolaDem", + Name = "WAC [Utah]", FilePath = mapServiceConfigs .. "/Utah/LolaDem.wms", Enabled = true, TilePixelSize = 64, diff --git a/data/assets/scene/solarsystem/planets/earth/transforms.asset b/data/assets/scene/solarsystem/planets/earth/transforms.asset index b51e5c8e9e..e93c136f2b 100644 --- a/data/assets/scene/solarsystem/planets/earth/transforms.asset +++ b/data/assets/scene/solarsystem/planets/earth/transforms.asset @@ -16,7 +16,8 @@ local EarthBarycenter = { }, GUI = { Name = "Earth Barycenter", - Path = "/Solar System/Planets/Earth" + Path = "/Solar System/Planets/Earth", + Hidden = true } } @@ -33,7 +34,8 @@ local EarthInertial = { }, GUI = { Name = "Earth Inertial", - Path = "/Solar System/Planets/Earth" + Path = "/Solar System/Planets/Earth", + Hidden = true } } @@ -49,7 +51,8 @@ local EarthIAU = { }, GUI = { Name = "Earth IAU", - Path = "/Solar System/Planets/Earth" + Path = "/Solar System/Planets/Earth", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/planets/jupiter/transforms.asset b/data/assets/scene/solarsystem/planets/jupiter/transforms.asset index 9b8ace2846..851a2d5310 100644 --- a/data/assets/scene/solarsystem/planets/jupiter/transforms.asset +++ b/data/assets/scene/solarsystem/planets/jupiter/transforms.asset @@ -16,7 +16,8 @@ local JupiterBarycenter = { }, GUI = { Name = "Jupiter Barycenter", - Path = "/Solar System/Planets/Jupiter" + Path = "/Solar System/Planets/Jupiter", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/planets/mars/transforms.asset b/data/assets/scene/solarsystem/planets/mars/transforms.asset index 9cba89eda4..c953e89c69 100644 --- a/data/assets/scene/solarsystem/planets/mars/transforms.asset +++ b/data/assets/scene/solarsystem/planets/mars/transforms.asset @@ -16,7 +16,8 @@ local MarsBarycenter = { }, GUI = { Name = "Mars Barycenter", - Path = "/Solar System/Planets/Mars" + Path = "/Solar System/Planets/Mars", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/planets/mercury/transforms.asset b/data/assets/scene/solarsystem/planets/mercury/transforms.asset index 7292d44afc..bef198f85d 100644 --- a/data/assets/scene/solarsystem/planets/mercury/transforms.asset +++ b/data/assets/scene/solarsystem/planets/mercury/transforms.asset @@ -16,7 +16,8 @@ local MercuryBarycenter = { }, GUI = { Name = "Mercury Barycenter", - Path = "/Solar System/Planets/Mercury" + Path = "/Solar System/Planets/Mercury", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/planets/neptune/transforms.asset b/data/assets/scene/solarsystem/planets/neptune/transforms.asset index 2c726221e2..c9671ca5b0 100644 --- a/data/assets/scene/solarsystem/planets/neptune/transforms.asset +++ b/data/assets/scene/solarsystem/planets/neptune/transforms.asset @@ -16,7 +16,8 @@ local NeptuneBarycenter = { }, GUI = { Name = "Neptune Barycenter", - Path = "/Solar System/Planets/Neptune" + Path = "/Solar System/Planets/Neptune", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/planets/neptune/triton.asset b/data/assets/scene/solarsystem/planets/neptune/triton.asset index eb025fddf3..92a57ab4cc 100644 --- a/data/assets/scene/solarsystem/planets/neptune/triton.asset +++ b/data/assets/scene/solarsystem/planets/neptune/triton.asset @@ -8,7 +8,7 @@ local kernels081 = asset.require('./nep081').Kernels local Triton = { Identifier = "Triton", Parent = { - Name = transforms.NeptuneBarycenter.Identifier, + Identifier = transforms.NeptuneBarycenter.Identifier, Spice = "NEPTUNE BARYCENTER" }, Spice = "TRITON", diff --git a/data/assets/scene/solarsystem/planets/saturn/transforms.asset b/data/assets/scene/solarsystem/planets/saturn/transforms.asset index 8a32be0a6d..910aae992f 100644 --- a/data/assets/scene/solarsystem/planets/saturn/transforms.asset +++ b/data/assets/scene/solarsystem/planets/saturn/transforms.asset @@ -16,7 +16,8 @@ local SaturnBarycenter = { }, GUI = { Name = "Saturn Barycenter", - Path = "/Solar System/Planets/Saturn" + Path = "/Solar System/Planets/Saturn", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/planets/uranus/transforms.asset b/data/assets/scene/solarsystem/planets/uranus/transforms.asset index f42a8750bc..2b801bebd1 100644 --- a/data/assets/scene/solarsystem/planets/uranus/transforms.asset +++ b/data/assets/scene/solarsystem/planets/uranus/transforms.asset @@ -16,7 +16,8 @@ local UranusBarycenter = { }, GUI = { Name = "Uranus Barycenter", - Path = "/Solar System/Planets/Uranus" + Path = "/Solar System/Planets/Uranus", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/planets/venus/trail.asset b/data/assets/scene/solarsystem/planets/venus/trail.asset index b0385f1e57..b9c3d86743 100644 --- a/data/assets/scene/solarsystem/planets/venus/trail.asset +++ b/data/assets/scene/solarsystem/planets/venus/trail.asset @@ -21,7 +21,8 @@ local VenusTrail = { Tag = { "planetTrail_solarSystem", "planetTrail_terrestrial" }, GUI = { Name = "Venus Trail", - Path = "/Solar System/Planets/Venus" + Path = "/Solar System/Planets/Venus", + Hidden = true } } diff --git a/data/assets/scene/solarsystem/sun/transforms.asset b/data/assets/scene/solarsystem/sun/transforms.asset index 0083c08083..d30fef8ae6 100644 --- a/data/assets/scene/solarsystem/sun/transforms.asset +++ b/data/assets/scene/solarsystem/sun/transforms.asset @@ -5,7 +5,12 @@ asset.require("spice/base") -- Barycenter of the solar system, expressed in the Galactic frame local SolarSystemBarycenter = { - Identifier = "SolarSystemBarycenter" + Identifier = "SolarSystemBarycenter", + GUI = { + Name = "Solar System Barycenter", + Path = "/Solar System", + Hidden = true + } -- No parent; this node is attached to the scene graph root } @@ -27,7 +32,8 @@ local SunIAU = { }, GUI = { Name = "SUN IAU", - Path = "/Solar System/Sun" + Path = "/Solar System/Sun", + Hidden = true } } diff --git a/data/assets/util/property_helper.asset b/data/assets/util/property_helper.asset index 8d3576d130..dab5038a2c 100644 --- a/data/assets/util/property_helper.asset +++ b/data/assets/util/property_helper.asset @@ -16,6 +16,35 @@ local decrement = function(prop, value) return increment(prop, -value) end +local fade = function(prop, value, duration) + assert(type(prop) == "string", "prop must be a number") + assert(type(duration) == "number", "duration must be a number") + + local escaped_property = "'" .. prop .. "'" + return "openspace.setPropertyValue(" .. escaped_property ..", " .. tostring(value) .. ", " .. tostring(duration) .. ")" +end + +local fadeOut = function(prop, duration) + return fade(prop, 0.0, duration) +end + +local fadeIn = function(prop, duration) + return fade(prop, 1.0, duration) +end + +local fadeInOut = function(prop, duration) + assert(type(prop) == "string", "prop must be a number") + assert(type(duration) == "number", "duration must be a number") + + local escaped_property = "'" .. prop .. "'" + -- If the value is > 0.5 fade out, otherwise fade in + return "local v = openspace.getPropertyValue(" .. escaped_property .. "); if v <= 0.5 then " .. fadeIn(prop, duration) .. " else " .. fadeOut(prop, duration) .. " end" +end + asset.export('invert', invert) asset.export('increment', increment) asset.export('decrement', decrement) +asset.export('fade', fade) +asset.export('fadeIn', fadeIn) +asset.export('fadeOut', fadeOut) +asset.export('fadeInOut', fadeInOut) diff --git a/include/openspace/scene/scenegraphnode.h b/include/openspace/scene/scenegraphnode.h index 164553d223..363d3bd659 100644 --- a/include/openspace/scene/scenegraphnode.h +++ b/include/openspace/scene/scenegraphnode.h @@ -137,6 +137,7 @@ public: Renderable* renderable(); const std::string& guiPath() const; + bool hasGuiHintHidden() const; static documentation::Documentation Documentation(); @@ -152,6 +153,10 @@ private: std::vector _dependentNodes; Scene* _scene; + // If this value is 'true' GUIs are asked to hide this node from collections, as it + // might be a node that is not very interesting (for example barycenters) + bool _guiHintHidden = false; + PerformanceRecord _performanceRecord; std::unique_ptr _renderable; diff --git a/modules/imgui/include/guipropertycomponent.h b/modules/imgui/include/guipropertycomponent.h index 160cf88bfc..8475dfad06 100644 --- a/modules/imgui/include/guipropertycomponent.h +++ b/modules/imgui/include/guipropertycomponent.h @@ -77,6 +77,7 @@ protected: properties::BoolProperty _useTreeLayout; properties::StringListProperty _treeOrdering; + properties::BoolProperty _ignoreHiddenHint; }; } // namespace openspace::gui diff --git a/modules/imgui/src/guipropertycomponent.cpp b/modules/imgui/src/guipropertycomponent.cpp index 8ca29ec472..1656a3a263 100644 --- a/modules/imgui/src/guipropertycomponent.cpp +++ b/modules/imgui/src/guipropertycomponent.cpp @@ -53,6 +53,14 @@ namespace { "elements not listed." }; + static const openspace::properties::Property::PropertyInfo IgnoreHiddenInfo = { + "IgnoreHidden", + "Ignore Hidden Hint", + "If this value is 'true', all 'Hidden' hints passed into the SceneGraphNodes are " + "ignored and thus all SceneGraphNodes are displayed. If this value is 'false', " + "the hidden hints are followed." + }; + int nVisibleProperties(const std::vector& props, openspace::properties::Property::Visibility visibility) { @@ -166,9 +174,11 @@ GuiPropertyComponent::GuiPropertyComponent(std::string identifier, std::string g : GuiComponent(std::move(identifier), std::move(guiName)) , _useTreeLayout(UseTreeInfo, useTree) , _treeOrdering(OrderingInfo) + , _ignoreHiddenHint(IgnoreHiddenInfo) { addProperty(_useTreeLayout); addProperty(_treeOrdering); + addProperty(_ignoreHiddenHint); } void GuiPropertyComponent::setSource(SourceFunction function) { @@ -378,6 +388,25 @@ void GuiPropertyComponent::render() { }; if (!_useTreeLayout || noGuiGroups) { + if (!_ignoreHiddenHint) { + // Remove all of the nodes that we want hidden first + owners.erase( + std::remove_if( + owners.begin(), + owners.end(), + [](properties::PropertyOwner* p) { + SceneGraphNode* s = dynamic_cast(p); + if (s && s->hasGuiHintHidden()) { + return true; + } + else { + return false; + } + } + ), + owners.end() + ); + } std::for_each(owners.begin(), owners.end(), renderProp); } else { // _useTreeLayout && gui groups exist @@ -386,6 +415,9 @@ void GuiPropertyComponent::render() { for (properties::PropertyOwner* pOwner : owners) { // We checked above that pOwner is a SceneGraphNode SceneGraphNode* nOwner = static_cast(pOwner); + if (!_ignoreHiddenHint && nOwner->hasGuiHintHidden()) { + continue; + } if (nOwner->guiPath().empty()) { // We know that we are done now since we stable_sort:ed them above diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index f7b10d582e..b390877b16 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -56,7 +56,7 @@ properties::PropertyOwner* findPropertyOwnerWithMatchingGroupTag(T* prop, return tagMatchOwner; } -void applyRegularExpression(lua_State* L, std::regex regex, +void applyRegularExpression(lua_State* L, const std::string& regex, std::vector properties, double interpolationDuration, const std::string& groupName, @@ -68,11 +68,15 @@ void applyRegularExpression(lua_State* L, std::regex regex, const int type = lua_type(L, -1); + // Stores whether we found at least one matching property. If this is false at the end + // of the loop, the property name regex was probably misspelled. + bool foundMatching = false; + std::regex r(regex); for (properties::Property* prop : properties) { // Check the regular expression for all properties std::string id = prop->fullyQualifiedIdentifier(); - if (std::regex_match(id, regex)) { + if (std::regex_match(id, r)) { // If the fully qualified id matches the regular expression, we queue the // value change if the types agree if (isGroupMode) { @@ -99,6 +103,8 @@ void applyRegularExpression(lua_State* L, std::regex regex, ) ); } else { + foundMatching = true; + if (interpolationDuration == 0.0) { OsEng.renderEngine().scene()->removeInterpolation(prop); prop->setLuaValue(L); @@ -114,6 +120,17 @@ void applyRegularExpression(lua_State* L, std::regex regex, } } } + + if (!foundMatching) { + LERRORC( + "property_setValue", + fmt::format( + "{}: No property matched the requested URI '{}'", + errorLocation(L), + regex + ) + ); + } } // Checks to see if URI contains a group tag (with { } around the first term). If so, @@ -270,7 +287,7 @@ int property_setValue(lua_State* L) { try { applyRegularExpression( L, - std::regex(uriOrRegex), + uriOrRegex, allProperties(), interpolationDuration, groupName, @@ -291,7 +308,7 @@ int property_setValue(lua_State* L) { try { applyRegularExpression( L, - std::regex(uriOrRegex), + uriOrRegex, allProperties(), interpolationDuration, "", diff --git a/src/scene/scenegraphnode.cpp b/src/scene/scenegraphnode.cpp index 5b80bb2bff..84f720dfc5 100644 --- a/src/scene/scenegraphnode.cpp +++ b/src/scene/scenegraphnode.cpp @@ -56,8 +56,9 @@ namespace { constexpr const char* _loggerCat = "SceneGraphNode"; constexpr const char* KeyRenderable = "Renderable"; - constexpr const char* KeyName = "GUI.Name"; + constexpr const char* KeyGuiName = "GUI.Name"; constexpr const char* KeyGuiPath = "GUI.Path"; + constexpr const char* KeyGuiHidden = "GUI.Hidden"; constexpr const char* keyTransformTranslation = "Transform.Translation"; constexpr const char* keyTransformRotation = "Transform.Rotation"; @@ -80,8 +81,12 @@ std::unique_ptr SceneGraphNode::createFromDictionary( std::string identifier = dictionary.value(KeyIdentifier); result->setIdentifier(std::move(identifier)); - if (dictionary.hasKey(KeyName)) { - result->setGuiName(dictionary.value(KeyName)); + if (dictionary.hasKey(KeyGuiName)) { + result->setGuiName(dictionary.value(KeyGuiName)); + } + + if (dictionary.hasKey(KeyGuiHidden)) { + result->_guiHintHidden = dictionary.value(KeyGuiHidden); } if (dictionary.hasKey(keyTransformTranslation)) { @@ -573,6 +578,10 @@ const std::string& SceneGraphNode::guiPath() const { return _guiPath; } +bool SceneGraphNode::hasGuiHintHidden() const { + return _guiHintHidden; +} + glm::dvec3 SceneGraphNode::calculateWorldPosition() const { // recursive up the hierarchy if there are parents available if (_parent) { diff --git a/src/scene/scenegraphnode_doc.inl b/src/scene/scenegraphnode_doc.inl index 4037bf8bf7..938d2322b2 100644 --- a/src/scene/scenegraphnode_doc.inl +++ b/src/scene/scenegraphnode_doc.inl @@ -43,13 +43,6 @@ documentation::Documentation SceneGraphNode::Documentation() { "detected the loading of the node will fail, as will all childing that " "depend on the node. The identifier must not contain any whitespaces or '.'." }, - { - "Name", - new StringVerifier, - Optional::Yes, - "An optional user-facing name for this SceneGraphNode, which does not have " - "to be unique, though it is recommended, and can contain any characters." - }, { "Parent", new StringAnnotationVerifier( @@ -106,13 +99,39 @@ documentation::Documentation SceneGraphNode::Documentation() { "corresponding to a 'Translation', a 'Rotation', and a 'Scale'." }, { - "GuiPath", - new StringVerifier, + "GUI", + new TableVerifier({ + { + "Name", + new StringVerifier, + Optional::Yes, + "An optional user-facing name for this SceneGraphNode, which does " + "not have to be unique, though it is recommended, and can contain " + "any characters." + }, + { + "Path", + new StringVerifier, + Optional::Yes, + "If this value is specified, this '/' separated URI specifies the " + "location of this scenegraph node in a GUI representation, for " + "instance '/SolarSystem/Earth/Moon'." + }, + { + "Hidden", + new BoolVerifier, + Optional::Yes, + "If this value is specified, GUI applications are incouraged to " + "ignore this scenegraph node. This is most useful to trim collective " + "lists of nodes and not display, for example, barycenters." + } + }), Optional::Yes, - "If this value is specified, this '/' separated URI specifies the location " - "of this scenegraph node in a GUI representation, for instance " - "'/SolarSystem/Earth/Moon'." - } + "Additional information that is passed to GUI applications. These are all " + "hints and do not have any impact on the actual function of the scenegraph " + "node." + }, + } }; }