Fix toggleFade for multiple property owners, and add openspace.propertyOwner API function (#3721)

* Add a function for getting all propertyowners matching a certain regex
* Fix broken toggleFade - now works with multiple nodes
* Utilize updated toggle fade function in existing toggle actions
* Make `openspace.propertyOwner` work when only tag is included
* And make toggleFade work in a situation where tag matches both a screenspace renderable and scene graph node
* Remove mistakenly added scene graph node (it is really a screenspace renderable)
* Cleanup property check code to reuse check in both property and propertyowner function
This commit is contained in:
Emma Broman
2025-06-18 18:32:17 +02:00
committed by GitHub
parent b6d3704439
commit b51a3322e2
20 changed files with 334 additions and 296 deletions
@@ -1,12 +1,7 @@
local ToggleDwarfPlanetTrails = {
Identifier = "os.ToggleDwarfPlanetTrails",
Name = "Toggle dwarf planet trails",
Command = [[
local list = openspace.property("{planetTrail_dwarf}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{planetTrail_dwarf}.Renderable")]],
Documentation = "Toggle on/off trails for all dwarf planets in the solar system",
GuiPath = "/Trails",
IsLocal = false
@@ -1,12 +1,7 @@
local ToggleMinorMoonTrails = {
Identifier = "os.ToggleMinorMoonTrails",
Name = "Toggle minor moon trails",
Command = [[
local list = openspace.property("{moonTrail_minor}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{moonTrail_minor}.Renderable")]],
Documentation = "Toggle on/off minor moon trails for all planets in the solar system",
GuiPath = "/Trails",
IsLocal = false
@@ -2,14 +2,8 @@ local FadeUpTrails = {
Identifier = "os.FadeUpTrails",
Name = "Show all trails",
Command = [[
local capList = openspace.property("Scene.*Trail.Renderable.Fade")
for _,v in ipairs(capList) do
openspace.setPropertyValueSingle(v, 1, 2)
end
local list = openspace.property("Scene.*trail.Renderable.Fade")
for _,v in ipairs(list) do
openspace.setPropertyValueSingle(v, 1, 2)
end
openspace.fadeIn("Scene.*Trail.Renderable")
openspace.fadeIn("Scene.*trail.Renderable")
]],
Documentation = "Fade up all enabled trails in the Scene",
GuiPath = "/Trails",
@@ -20,14 +14,8 @@ local FadeDownTrails = {
Identifier = "os.FadeDownTrails",
Name = "Hide all trails",
Command = [[
local capList = openspace.property("Scene.*Trail.Renderable.Fade")
for _,v in ipairs(capList) do
openspace.setPropertyValueSingle(v, 0, 2)
end
local list = openspace.property("Scene.*trail.Renderable.Fade")
for _,v in ipairs(list) do
openspace.setPropertyValueSingle(v, 0, 2)
end
openspace.fadeOut("Scene.*Trail.Renderable")
openspace.fadeOut("Scene.*trail.Renderable")
]],
Documentation = "Fade down all enabled trails in the Scene",
GuiPath = "/Trails",
@@ -38,30 +26,8 @@ local ToggleTrails = {
Identifier = "os.ToggleTrails",
Name = "Toggle all trails",
Command = [[
local capList = openspace.property("*Trail.Renderable.Fade")
local list = openspace.property("*trail.Renderable.Fade")
if (#capList == 0) and (#list == 0) then
openspace.printWarning("No trails to toggle")
else
local prop
if #capList > 0 then
prop = capList[1]
else
prop = list[1]
end
local currentFade = openspace.propertyValue(prop)
local newFade = 0
if currentFade < 1 then
newFade = 1
end
if (#capList > 0) then
openspace.setPropertyValue("Scene.*Trail.Renderable.Fade", newFade, 2)
end
if (#list > 0) then
openspace.setPropertyValue("Scene.*trail.Renderable.Fade", newFade, 2)
end
openspace.setPropertyValue("Scene.*TrailEarth.Renderable.Fade", newFade, 2)
end
openspace.toggleFade("Scene.*Trail.Renderable")
openspace.toggleFade("Scene.*trail.Renderable")
]],
Documentation = "Toggle fade for all trails in the Scene",
GuiPath = "/Trails",
@@ -72,29 +38,8 @@ local ToggleTrailsInstant = {
Identifier = "os.ToggleTrailsInstant",
Name = "Toggle all trails instantly",
Command = [[
local capList = openspace.property("*Trail.Renderable.Fade")
local list = openspace.property("*trail.Renderable.Fade")
if (#capList == 0) and (#list == 0) then
openspace.printWarning("No trails to toggle")
else
local prop
if #capList > 0 then
prop = capList[1]
else
prop = list[1]
end
local currentFade = openspace.propertyValue(prop)
local newFade = 0
if currentFade < 1 then
newFade = 1
end
if (#capList > 0) then
openspace.setPropertyValue("Scene.*Trail.Renderable.Fade", newFade)
end
if (#list > 0) then
openspace.setPropertyValue("Scene.*trail.Renderable.Fade", newFade)
end
end
openspace.toggleFade("Scene.*Trail.Renderable", 0.0)
openspace.toggleFade("Scene.*trail.Renderable", 0.0)
]],
Documentation = "Toggle fade instantly for all trails in the Scene",
GuiPath = "/Trails",
@@ -2,15 +2,8 @@ local ToggleTrailsInstant = {
Identifier = "os.ToggleTrailsInstant",
Name = "Toggle planet and moon trails (instant)",
Command = [[
local list = openspace.property("{planetTrail_solarSystem}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
local moonlist = openspace.property("{moonTrail_solarSystem}.Renderable.Enabled")
for _,v in pairs(moonlist) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
openspace.toggleFade("{planetTrail_solarSystem}.Renderable", 0.0)
openspace.toggleFade("{moonTrail_solarSystem}.Renderable", 0.0)
]],
Documentation = "Toggles the visibility of planet and moon trails",
GuiPath = "/Solar System",
@@ -2,8 +2,8 @@ local FadeUpTrails = {
Identifier = "os.planetsmoons.FadeUpTrails",
Name = "Show planet and moon trails",
Command = [[
openspace.setPropertyValue("{planetTrail_solarSystem}.Renderable.Fade", 1, 2)
openspace.setPropertyValue("{moonTrail_solarSystem}.Renderable.Fade", 1, 2)
openspace.fadeIn("{planetTrail_solarSystem}.Renderable")
openspace.fadeIn("{moonTrail_solarSystem}.Renderable")
]],
Documentation = "Fade up all planet and moon trails in the Scene",
GuiPath = "/Trails",
@@ -14,8 +14,8 @@ local FadeDownTrails = {
Identifier = "os.planetsmoons.FadeDownTrails",
Name = "Hide planet and moon trails",
Command = [[
openspace.setPropertyValue("{planetTrail_solarSystem}.Renderable.Fade", 0, 2)
openspace.setPropertyValue("{moonTrail_solarSystem}.Renderable.Fade", 0, 2)
openspace.fadeOut("{planetTrail_solarSystem}.Renderable")
openspace.fadeOut("{moonTrail_solarSystem}.Renderable")
]],
Documentation = "Fade down all planet and moon trails in the Scene",
GuiPath = "/Trails",
@@ -26,25 +26,8 @@ local ToggleTrails = {
Identifier = "os.planetsmoons.ToggleTrails",
Name = "Toggle planet and moon trails",
Command = [[
local capList = openspace.property("{planetTrail_solarSystem}.Renderable.Fade")
local list = openspace.property("{moonTrail_solarSystem}.Renderable.Fade")
if (#capList == 0) and (#list == 0) then
openspace.printWarning("No trails to toggle")
else
local prop
if (#capList > 0) then
prop = capList[1]
else
prop = list[1]
end
local currentFade = openspace.propertyValue(prop)
local newFade = 0
if currentFade < 1 then
newFade = 1
end
openspace.setPropertyValue("{planetTrail_solarSystem}.Renderable.Fade", newFade, 2)
openspace.setPropertyValue("{moonTrail_solarSystem}.Renderable.Fade", newFade, 2)
end
openspace.toggleFade("{planetTrail_solarSystem}.Renderable.Fade")
openspace.toggleFade("{moonTrail_solarSystem}.Renderable.Fade")
]],
Documentation = "Toggle fade for planet and moon trails in the Scene",
GuiPath = "/Trails",
+1 -6
View File
@@ -7,12 +7,7 @@ local allTrailsInstantAction = asset.require("actions/trails/toggle_all_trails")
local TogglePlanetLabels = {
Identifier = "os_default.TogglePlanetLabels",
Name = "Toggle planet labels",
Command = [[
local list = openspace.property("{solarsystem_labels}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{solarsystem_labels}.Renderable")]],
Documentation = "Turns on visibility for all solar system labels",
GuiPath = "/Solar System",
IsLocal = false
@@ -1,12 +1,7 @@
local ToggleOuterPlanetaryTrails = {
Identifier = "os.rosetta.ToggleOuterPlanetaryTrails",
Name = "Toggle outer planetary trails",
Command = [[
local list = openspace.property("{planetTrail_giants}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{planetTrail_giants}.Renderable")]],
Documentation = "Toggles the visibility of all trails further from the Sun than 67P",
GuiPath = "/Missions/Rosetta",
IsLocal = false
@@ -49,12 +49,7 @@ local SaturnFocus = {
local ToggleMinorMoonTrails = {
Identifier = "os.voyager.ToggleMinorMoonTrails",
Name = "Toggle minor trails",
Command = [[
local list = openspace.property("{moonTrail_minor}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{moonTrail_minor}.Renderable")]],
Documentation = "Toggles the trails of the minor moons",
GuiPath = "/Trails",
IsLocal = false
@@ -91,14 +91,7 @@ local FocusEarth = {
local ToggleSatelliteTrails = {
Identifier = "os.solarsystem.ToggleSatelliteTrails",
Name = "Toggle satellite trails",
Command = [[
local list = openspace.property(
"{earth_satellites~space_stations}.Renderable.Enabled"
)
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{earth_satellites~space_stations}.Renderable")]],
Documentation = [[
Toggle trails on or off for the satellites around Earth (excluding space stations)
]],
@@ -109,12 +102,7 @@ local ToggleSatelliteTrails = {
local ToggleSpaceStations = {
Identifier = "os.solarsystem.ToggleSpaceStations",
Name = "Toggle space stations",
Command = [[
local list = openspace.property("{space_stations}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{space_stations}.Renderable")]],
Documentation = [[
Toggle visiblity of the space stations around Earth (including their trail).
]],
@@ -125,12 +113,7 @@ local ToggleSpaceStations = {
local ToggleSpaceStationTrails = {
Identifier = "os.solarsystem.ToggleSpaceStationTrails",
Name = "Toggle space station trails",
Command = [[
local list = openspace.property("{space_stations&trails}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{space_stations&trails}.Renderable")]],
Documentation = [[
Toggle visiblity of the trails for the space stations around Earth.
]],
@@ -1,12 +1,7 @@
local ToggleLagrangianPoints = {
Identifier = "os.jwst.ToggleLagrangianPoints",
Name = "Toggle Lagrangian points",
Command = [[
local list = openspace.property("{lagrange_points_earth}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{lagrange_points_earth}.Renderable")]],
Documentation = "Toggle points and labels for the Lagrangian points for Earth Sun system",
GuiPath = "/JWST",
IsLocal = false
@@ -15,11 +10,11 @@ local ToggleLagrangianPoints = {
local ToggleHudf = {
Identifier = "os.jwst.ToggleHudf",
Name = "Toggle Hubble Ultra Deep Field",
-- The tag used here matches both a screenspace renderable and a scene graph node,
-- hence the need for the double toggleFade
Command = [[
local list = openspace.property("{mission_jwst_hudf}.*.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
openspace.toggleFade("{mission_jwst_hudf}")
openspace.toggleFade("{mission_jwst_hudf}.Renderable")
]],
Documentation = "Toggle Hubble Ultra Deep Field image and line towards its coordinate",
GuiPath = "/JWST",
@@ -29,12 +24,7 @@ local ToggleHudf = {
local ToggleL2 = {
Identifier = "os.jwst.ToggleL2",
Name = "Toggle L2 line and small L2 label",
Command = [[
local list = openspace.property("{lagrange_points_earth_l2_small}.*.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{lagrange_points_earth_l2_small}.Renderable")]],
Documentation = "Toggle L2 label, point and line",
GuiPath = "/JWST",
IsLocal = false
@@ -43,12 +33,7 @@ local ToggleL2 = {
local ToggleFov = {
Identifier = "os.jwst.ToggleFov",
Name = "Toggle JWST field of view and view band",
Command = [[
local list = openspace.property("{mission_jwst_fov}.*.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
]],
Command = [[openspace.toggleFade("{mission_jwst_fov}.Renderable")]],
Documentation = "Toggle James Webb Space Telecope field of view and view band",
GuiPath = "/JWST",
IsLocal = false
@@ -82,8 +67,7 @@ local ToggleSunTrail = {
Identifier = "os.jwst.ToggleSunTrail",
Name = "Toggle JWST Sun trail",
Command = [[
local value = openspace.propertyValue("Scene.JWSTSunTrail.Renderable.Enabled")
openspace.setPropertyValueSingle("Scene.JWSTSunTrail.Renderable.Enabled", not value)
openspace.toggleFade("Scene.JWSTSunTrail.Renderable")
]],
Documentation = "Toggle JWST trail relative to the Sun",
GuiPath = "/JWST",
@@ -94,15 +78,8 @@ local ToggleTrailsExceptMoon = {
Identifier = "os.jwst.ToggleTrailsExceptMoon",
Name = "Toggle trails (except Moon)",
Command = [[
local list = openspace.property("{planetTrail_solarSystem}.Renderable.Enabled")
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
local moonlist = openspace.property("{moonTrail_solarSystem}.Renderable.Enabled")
for _,v in pairs(moonlist) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
openspace.setPropertyValueSingle("Scene.MoonTrail.Renderable.Enabled", true)
openspace.toggleFade("{planetTrail_solarSystem}.Renderable")
openspace.toggleFade("{moonTrail_solarSystem~moon_earth}.Renderable")
]],
Documentation = "Toggle all planet and moon trails, except the Moon",
GuiPath = "/JWST",
@@ -113,14 +90,9 @@ local ToggleJwstTrails = {
Identifier = "os.jwst.ToggleJwstTrails",
Name = "Toggle JWST trail",
Command = [[
local list = {
"Scene.JWSTTrailLaunch.Renderable.Enabled",
"Scene.JWSTTrailCruise.Renderable.Enabled",
"Scene.JWSTTrailCoRevOrbit.Renderable.Enabled"
}
for _,v in pairs(list) do
openspace.setPropertyValueSingle(v, not openspace.propertyValue(v))
end
openspace.toggleFade("Scene.JWSTTrailLaunch.Renderable")
openspace.toggleFade("Scene.JWSTTrailCruise.Renderable")
openspace.toggleFade("Scene.JWSTTrailCoRevOrbit.Renderable")
]],
Documentation = "Toggle JWST launch, cruise and L2 co-revolving orbit trails, not the Sun trail",
GuiPath = "/JWST",
@@ -77,12 +77,10 @@ asset.onInitialize(function()
openspace.addScreenSpaceRenderable(HUDFImage)
openspace.addSceneGraphNode(HUDFPosition)
openspace.addSceneGraphNode(HUDFImage)
openspace.addSceneGraphNode(HUDFJWSTLine)
end)
asset.onDeinitialize(function()
openspace.removeSceneGraphNode(HUDFPosition)
openspace.removeSceneGraphNode(HUDFImage)
openspace.removeSceneGraphNode(HUDFJWSTLine)
openspace.removeScreenSpaceRenderable(HUDFImage)
@@ -135,7 +135,9 @@ public:
LoadingScreen* loadingScreen();
void invalidatePropertyCache();
void invalidatePropertyOwnerCache();
const std::vector<properties::Property*>& allProperties() const;
const std::vector<properties::PropertyOwner*>& allPropertyOwners() const;
void createUserDirectoriesIfNecessary();
@@ -182,6 +184,9 @@ private:
mutable bool _isAllPropertiesCacheDirty = true;
mutable std::vector<properties::Property*> _allPropertiesCache;
mutable bool _isAllPropertyOwnersCacheDirty = true;
mutable std::vector<properties::PropertyOwner*> _allPropertyOwnersCache;
};
/**
+15 -1
View File
@@ -138,6 +138,15 @@ public:
*/
std::vector<Property*> propertiesRecursive() const;
/**
* Returns a list of all PropertyOwners directly or indirectly owned by this
* PropertyOwner.
*
* \return A list of all PropertyOwners directly or indirectly owned by this
* PropertyOwner
*/
std::vector<PropertyOwner*> subownersRecursive() const;
/**
* Retrieves a Property identified by \p uri from this PropertyOwner. If \p uri does
* not contain a `.` it is an identifier and must refer to a Property directly owned
@@ -338,9 +347,14 @@ protected:
/// Collection of string tag(s) assigned to this property
std::vector<std::string> _tags;
/// A cached version of the full URI of this property owner, which includes the
/// identifiers of all owners
std::string _uriCache;
bool _isUriCacheDirty = true;
private:
/// Will regenerate the uri caches for all directly or indirectly owned properties
/// Will regenerate the uri caches for this property owner and all directly or
/// indirectly owned properties
void updateUriCaches();
};
+1
View File
@@ -44,6 +44,7 @@ const Renderable* renderable(const std::string& name);
properties::Property* property(const std::string& uri);
properties::PropertyOwner* propertyOwner(const std::string& uri);
const std::vector<properties::Property*>& allProperties();
const std::vector<properties::PropertyOwner*>& allPropertyOwners();
} // namespace openspace
+73 -61
View File
@@ -206,49 +206,45 @@ openspace.fadeIn = function(identifier, fadeTime, endScript)
local hasTag, _ = identifier:find("{")
local hasWild, _ = identifier:find("*")
-- Check if the properties exist
local hasEnabled = true
local hasFade = true
if hasTag ~= nil or hasWild ~= nil then
-- Regex, several nodes
local enabledPropertyList = openspace.property(enabledProperty)
if next(enabledPropertyList) == nil then
-- List is empty, no matches found
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find any property matching '" .. enabledProperty .. "'"
)
return
hasEnabled = false
end
local fadePropertyList = openspace.property(fadeProperty)
if next(fadePropertyList) == nil then
-- List is empty, no matches found
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find any property matching '" .. fadeProperty .. "'"
)
return
hasFade = false
end
else
-- Literal, single node
local hasEnabled = openspace.hasProperty(enabledProperty)
local hasFade = openspace.hasProperty(fadeProperty)
if not hasEnabled then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. enabledProperty .. "'"
)
return
elseif not hasFade then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. fadeProperty .. "'"
)
return
else
hasEnabled = openspace.hasProperty(enabledProperty)
hasFade = openspace.hasProperty(fadeProperty)
if hasEnabled then
isEnabled = openspace.propertyValue(enabledProperty)
end
end
if not hasEnabled then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. enabledProperty .. "'"
)
return
elseif not hasFade then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. fadeProperty .. "'"
)
return
end
-- If node is already enabled we only have to fade it
if not isEnabled then
openspace.setPropertyValue(fadeProperty, 0.0)
@@ -273,49 +269,45 @@ openspace.fadeOut = function(identifier, fadeTime, endScript)
local hasTag, _ = identifier:find("{")
local hasWild, _ = identifier:find("*")
-- Check if the properties exist
local hasEnabled = true
local hasFade = true
if hasTag ~= nil or hasWild ~= nil then
-- Regex, several nodes
local enabledPropertyList = openspace.property(enabledProperty)
if next(enabledPropertyList) == nil then
-- List is empty, no matches found
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find any property matching '" .. enabledProperty .. "'"
)
return
hasEnabled = false
end
local fadePropertyList = openspace.property(fadeProperty)
if next(fadePropertyList) == nil then
-- List is empty, no matches found
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find any property matching '" .. fadeProperty .. "'"
)
return
hasFade = false
end
else
-- Literal, single node
local hasEnabled = openspace.hasProperty(enabledProperty)
local hasFade = openspace.hasProperty(fadeProperty)
if not hasEnabled then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. enabledProperty .. "'"
)
return
elseif not hasFade then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. fadeProperty .. "'"
)
return
else
hasEnabled = openspace.hasProperty(enabledProperty)
hasFade = openspace.hasProperty(fadeProperty)
if hasEnabled then
isEnabled = openspace.propertyValue(enabledProperty)
end
end
if not hasEnabled then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. enabledProperty .. "'"
)
return
elseif not hasFade then
openspace.printError(
"Error when calling script 'openspace.fadeIn': " ..
"Could not find property '" .. fadeProperty .. "'"
)
return
end
-- If node is already disabled we don't have to do anything
if isEnabled then
local disableScript = "openspace.setPropertyValue('" .. enabledProperty .. "', false)"
@@ -334,17 +326,37 @@ openspace.toggleFade = function(renderable, fadeTime, endScript)
fadeTime = openspace.propertyValue("OpenSpaceEngine.FadeDuration")
end
local enabled = openspace.propertyValue(renderable .. ".Enabled")
local fadeState = openspace.propertyValue(renderable .. ".Fade")
local renderablesList = openspace.propertyOwner(renderable)
if (enabled) then
if (fadeState < 0.5) then
openspace.fadeIn(renderable, fadeTime-(fadeTime*fadeState), endScript)
else
openspace.fadeOut(renderable, fadeTime*fadeState, endScript)
if next(renderablesList) == nil then
-- List is empty, no matches found
openspace.printError(
"Error when calling script 'openspace.toggleFade': " ..
"Could not find any property owner matching '" .. renderable .. "'"
)
return
end
for i = 1, #renderablesList do
local renderable = renderablesList[i]
-- Skip any matches that do not have the properties we need
if openspace.hasProperty(renderable .. ".Enabled") and
openspace.hasProperty(renderable .. ".Fade")
then
local enabled = openspace.propertyValue(renderable .. ".Enabled")
local fadeState = openspace.propertyValue(renderable .. ".Fade")
if (enabled) then
if (fadeState < 0.5) then
openspace.fadeIn(renderable, fadeTime-(fadeTime*fadeState), endScript)
else
openspace.fadeOut(renderable, fadeTime*fadeState, endScript)
end
else
openspace.fadeIn(renderable, fadeTime, endScript)
end
end
else
openspace.fadeIn(renderable, fadeTime, endScript)
end
end
+14
View File
@@ -1753,6 +1753,10 @@ void OpenSpaceEngine::invalidatePropertyCache() {
_isAllPropertiesCacheDirty = true;
}
void OpenSpaceEngine::invalidatePropertyOwnerCache() {
_isAllPropertyOwnersCacheDirty = true;
}
const std::vector<properties::Property*>& OpenSpaceEngine::allProperties() const {
if (_isAllPropertiesCacheDirty) {
_allPropertiesCache = global::rootPropertyOwner->propertiesRecursive();
@@ -1762,6 +1766,16 @@ const std::vector<properties::Property*>& OpenSpaceEngine::allProperties() const
return _allPropertiesCache;
}
const std::vector<properties::PropertyOwner*>& OpenSpaceEngine::allPropertyOwners() const
{
if (_isAllPropertyOwnersCacheDirty) {
_allPropertyOwnersCache = global::rootPropertyOwner->subownersRecursive();
_isAllPropertyOwnersCacheDirty = false;
}
return _allPropertyOwnersCache;
}
AssetManager& OpenSpaceEngine::assetManager() {
ghoul_assert(_assetManager, "Asset Manager must not be nullptr");
return *_assetManager;
+16
View File
@@ -95,6 +95,17 @@ std::vector<Property*> PropertyOwner::propertiesRecursive() const {
return props;
}
std::vector<PropertyOwner*> PropertyOwner::subownersRecursive() const {
std::vector<PropertyOwner*> subowners = _subOwners;
for (const PropertyOwner* owner : _subOwners) {
std::vector<PropertyOwner*> p = owner->subownersRecursive();
subowners.insert(subowners.end(), p.begin(), p.end());
}
return subowners;
}
Property* PropertyOwner::property(const std::string& uri) const {
auto it = std::find_if(
_properties.begin(),
@@ -323,6 +334,7 @@ void PropertyOwner::addPropertySubOwner(openspace::properties::PropertyOwner* ow
updateUriCaches();
if (global::openSpaceEngine) {
global::openSpaceEngine->invalidatePropertyCache();
global::openSpaceEngine->invalidatePropertyOwnerCache();
}
// Notify change so UI gets updated
@@ -392,6 +404,10 @@ void PropertyOwner::removePropertySubOwner(openspace::properties::PropertyOwner*
global::openSpaceEngine->invalidatePropertyCache();
}
_subOwners.erase(it);
if (global::openSpaceEngine) {
global::openSpaceEngine->invalidatePropertyOwnerCache();
}
}
else {
LERROR(std::format(
+4
View File
@@ -65,4 +65,8 @@ const std::vector<properties::Property*>& allProperties() {
return global::openSpaceEngine->allProperties();
}
const std::vector<properties::PropertyOwner*>& allPropertyOwners() {
return global::openSpaceEngine->allPropertyOwners();
}
} // namespace openspace
+1
View File
@@ -978,6 +978,7 @@ at the end of the interpolation. If 0 was provided, the script runs immediately.
},
codegen::lua::HasProperty,
codegen::lua::Property,
codegen::lua::PropertyOwner,
codegen::lua::AddCustomProperty,
codegen::lua::RemoveCustomProperty,
codegen::lua::AddSceneGraphNode,
+168 -46
View File
@@ -68,7 +68,7 @@ namespace {
* and if that does not contain the requested tag, its own owners are checked recursively.
*/
bool ownerMatchesGroupTag(const openspace::properties::PropertyOwner* owner,
std::string_view tagToMatch)
std::string_view tagToMatch, bool recursive = true)
{
using namespace openspace;
@@ -171,9 +171,11 @@ bool ownerMatchesGroupTag(const openspace::properties::PropertyOwner* owner,
}
// If we got this far, we have an owner and we haven't found a match, so we have to
// try one level higher
ghoul_assert(owner, "Owner does not exist");
return ownerMatchesGroupTag(owner->owner(), tagToMatch);
// try one level higher if we are checking recursively
if (recursive) {
return ownerMatchesGroupTag(owner->owner(), tagToMatch);
}
return false;
}
// Checks to see if URI contains a group tag (with { } around the first term)
@@ -229,6 +231,61 @@ std::tuple<std::string_view, std::string_view, bool> parseRegex(std::string_view
}
}
bool checkUriMatchFromRegexResults(std::string_view uri,
std::tuple<std::string_view, std::string_view, bool> regexResults,
std::string_view groupTag,
const openspace::properties::PropertyOwner* parentOwner
)
{
const bool isGroupMode = !groupTag.empty();
auto [parentUri, identifier, isLiteral] = regexResults;
// Literal check
if (isLiteral && uri != identifier) {
return false;
}
if (!identifier.empty()) {
const size_t propertyPos = uri.find(identifier);
if (
// Check if the identifier appears in the URI at all
(propertyPos == std::string::npos) ||
// Check that the identifier fully matches the identifier in URI
((propertyPos + identifier.length() + 1) < uri.length()) ||
// Match parent URI
(!parentUri.empty() && uri.find(parentUri) == std::string::npos))
{
return false;
}
// At this point we know that the identifier matches, so another way this
// property or property owner to fail is if we provided a tag and the owner
// doesn't match it
if (isGroupMode && !ownerMatchesGroupTag(parentOwner, groupTag)) {
return false;
}
}
else if (!parentUri.empty()) {
const size_t parentPos = uri.find(parentUri);
if (parentPos == std::string::npos) {
return false;
}
// Check tag
if (isGroupMode) {
if (!ownerMatchesGroupTag(parentOwner, groupTag)) {
return false;
}
}
else if (parentPos != 0) {
// Check that the node identifier fully matches the node in URI
return false;
}
}
return true;
}
std::vector<openspace::properties::Property*> findMatchesInAllProperties(
std::string_view regex,
std::string_view groupTag)
@@ -236,10 +293,10 @@ std::vector<openspace::properties::Property*> findMatchesInAllProperties(
using namespace openspace;
using namespace properties;
auto [nodeName, propertyIdentifier, isLiteral] = parseRegex(regex);
auto [parentUri, propertyIdentifier, isLiteral] = parseRegex(regex);
const bool isGroupMode = !groupTag.empty();
if (nodeName.empty() && isGroupMode) {
if (parentUri.empty() && isGroupMode) {
isLiteral = false;
}
@@ -255,50 +312,76 @@ std::vector<openspace::properties::Property*> findMatchesInAllProperties(
[&](Property* prop) {
const std::string_view uri = prop->uri();
if (isLiteral && uri != propertyIdentifier) {
return;
bool isMatch = checkUriMatchFromRegexResults(
uri,
{ parentUri, propertyIdentifier, isLiteral },
groupTag,
prop->owner()
);
if (isMatch) {
std::lock_guard g(mutex);
matches.push_back(prop);
}
}
);
if (!propertyIdentifier.empty()) {
const size_t propertyPos = uri.find(propertyIdentifier);
if (
// Check if the propertyIdentifier appears in the URI at all
(propertyPos == std::string::npos) ||
// Check that the propertyIdentifier fully matches the property in URI
((propertyPos + propertyIdentifier.length() + 1) < uri.length()) ||
// Match node name
(!nodeName.empty() && uri.find(nodeName) == std::string::npos))
{
return;
}
return matches;
}
// At this point we know that the property name matches, so another way
// this property to fail is if we provided a tag and the owner doesn't
// match it
if (isGroupMode && !ownerMatchesGroupTag(prop->owner(), groupTag)) {
std::vector<openspace::properties::PropertyOwner*> findMatchesInAllPropertyOwners(
std::string_view regex,
std::string_view groupTag)
{
using namespace openspace;
using namespace properties;
auto [parentUri, ownerIdentifier, isLiteral] = parseRegex(regex);
const bool isGroupMode = !groupTag.empty();
if (isGroupMode) {
isLiteral = false;
}
// If we are in group mode, no parent URI is found, and there is no punctuation
// in the returned owner identifier, we only got the group - no identifier
const bool inputIsOnlyTag = isGroupMode && parentUri.empty() &&
ownerIdentifier.find(".") == std::string::npos;
const std::vector<PropertyOwner*>& propertyOwners = allPropertyOwners();
std::vector<PropertyOwner*> matches;
std::mutex mutex;
std::for_each(
std::execution::par_unseq,
propertyOwners.cbegin(),
propertyOwners.cend(),
[&](PropertyOwner* propOwner) {
if (inputIsOnlyTag) {
// If we only got a tag as input, the result is all owners that directly
// match the group tag (without looking recusively in parent owners)
if (!ownerMatchesGroupTag(propOwner, groupTag, false)) {
return;
}
}
else if (!nodeName.empty()) {
const size_t nodePos = uri.find(nodeName);
if (nodePos == std::string::npos) {
return;
}
else {
const std::string uri = propOwner->uri();
// Check tag
if (isGroupMode) {
if (!ownerMatchesGroupTag(prop->owner(), groupTag)) {
return;
}
}
else if (nodePos != 0) {
// Check that the node identifier fully matches the node in URI
bool isMatch = checkUriMatchFromRegexResults(
uri,
{ parentUri, ownerIdentifier, isLiteral },
groupTag,
propOwner->owner()
);
if (!isMatch) {
return;
}
}
std::lock_guard g(mutex);
matches.push_back(prop);
matches.push_back(propOwner);
}
);
@@ -631,15 +714,17 @@ namespace {
* `uri` identifies the property or properties that are returned by this function and can
* include both wildcards `*` which match anything, as well as tags (`{tag}`) which match
* scene graph nodes that have this tag. There is also the ability to combine two tags
* through the `&`, `|`, and `~` operators. `{tag1&tag2}` will match anything that has the
* tag1 and the tag2. `{tag1|tag2}` will match anything that has the tag1 or the tag 2,
* and `{tag1~tag2}` will match anything that has tag1 but not tag2. If no wildcards or
* tags are provided at most one property value will be changed. With wildcards or tags
* all properties that match the URI are changed instead.
* through the `&`, `|`, and `~` operators. `{tag1&tag2}` will match anything that has
* both tags `tag1` and `tag2`. `{tag1|tag2}` will match anything that has `tag1` or
* `tag2`, and `{tag1~tag2}` will match anything that has `tag1` but not `tag2`. If no
* wildcards or tags are provided at most one property identifier will be returned. With
* wildcards or tags, the identifiers of all properties that match the URI are returned
* instead.
*
* \param uri The URI that identifies the property or properties whose values should be
* changed. The URI can contain 0 or 1 wildcard `*` characters or a tag
* expression (`{tag}`) that identifies a property owner.
* \param uri The URI that identifies the property or properties to get. The URI can
* contain 0 or 1 wildcard `*` characters or a tag expression (`{tag}`) that
* identifies a property owner.
* \ return A list of property URIs
*/
[[codegen::luawrap]] std::vector<std::string> property(std::string uri) {
using namespace openspace;
@@ -660,6 +745,43 @@ namespace {
return matches;
}
/**
* Returns a list of property owner identifiers that match the passed regular expression.
* The `uri` identifies the property owner or owner that are returned by this function and
* can include both wildcards `*` which match anything, as well as tags (`{tag}`) which
* match scene graph nodes that have this tag. There is also the ability to combine two
* tags through the `&`, `|`, and `~` operators. `{tag1&tag2}` will match anything that
* has both tags `tag1` and `tag2`. `{tag1|tag2}` will match anything that has the tag
* `tag1` or `tag2`, * and `{tag1~tag2}` will match anything that has `tag1` but not
* `tag2`. If no wildcards or tags are provided at most one property owner identifier
* will be returned. With wildcards or tags, the identifiers of all property owners that
* match the URI are returned instead.
*
* \param uri The URI that identifies the property owner or owners to get. The URI can
* contain 0 or 1 wildcard `*` characters or a tag expression (`{tag}`) that
* identifies a property owner.
* \ return A list of property owner URIs
*/
[[codegen::luawrap]] std::vector<std::string> propertyOwner(std::string uri) {
using namespace openspace;
std::string tag = groupTag(uri);
if (!tag.empty()) {
// Remove group name from start of regex and replace with '*'
uri = removeGroupTagFromUri(uri);
}
std::vector<properties::PropertyOwner*> owners =
findMatchesInAllPropertyOwners(uri, tag);
std::vector<std::string> matches;
matches.reserve(owners.size());
for (properties::PropertyOwner* owner : owners) {
matches.emplace_back(owner->uri());
}
return matches;
}
/**
* Loads the SceneGraphNode described in the table and adds it to the SceneGraph.
*/