Merge pull request #285 from OpenSpace/feature/grouping

Feature/grouping
This commit is contained in:
Gene Payne
2017-04-13 13:31:07 -06:00
committed by GitHub
16 changed files with 352 additions and 112 deletions

View File

@@ -49,6 +49,7 @@ return {
MieColor = {1.0, 1.0, 1.0}
}
},
Tag = {"planet_solarSystem", "planet_terrestrial"},
Transform = {
Rotation = {
Type = "SpiceRotation",
@@ -78,9 +79,10 @@ return {
-- EndTime = "2017 JAN 01 12:00:00.000",
-- SampleInterval = 3600
Period = 365.242,
Resolution = 1000
Resolution = 1000,
Tag = {"planetTrail_solarSystem", "planetTrail_terrestrial"}
},
GuiName = "/Solar/EarthTrail"
GuiName = "/Solar/EarthTrail",
},
--[[
{

View File

@@ -35,6 +35,7 @@ return {
MieColor = {1.0, 1.0, 1.0}
}
},
Tag = "planet_solarSystem",
Transform = {
Translation = {
Type = "StaticTranslation",
@@ -64,7 +65,8 @@ return {
},
Color = { 0.8, 0.7, 0.7 },
Period = 4330.595,
Resolution = 1000
}
Resolution = 1000,
},
Tag = "planetTrail_solarSystem"
}
}

View File

@@ -35,12 +35,17 @@ return {
MieColor = {1.0, 1.0, 1.0}
}
},
Tag = {"planet_solarSystem", "planet_terrestrial"},
Transform = {
Rotation = {
Type = "SpiceRotation",
SourceFrame = "IAU_MARS",
DestinationFrame = "GALACTIC",
},
Scale = {
Type = "StaticScale",
Scale = 1,
},
}
},
-- MarsTrail module
@@ -56,7 +61,8 @@ return {
},
Color = { 0.814, 0.305, 0.220 },
Period = 686.973,
Resolution = 1000
Resolution = 1000,
Tag = {"planetTrail_solarSystem", "planetTrail_terrestrial"}
}
}
}

View File

@@ -37,6 +37,7 @@ return {
MieColor = {1.0, 1.0, 1.0}
}
},
Tag = {"planet_solarSystem", "planet_terrestrial"},
Transform = {
Rotation = {
Type = "SpiceRotation",
@@ -62,7 +63,8 @@ return {
},
Color = {0.6, 0.5, 0.5 },
Period = 87.968,
Resolution = 100
Resolution = 100,
Tag = {"planetTrail_solarSystem", "planetTrail_terrestrial"}
}
}
}

View File

@@ -29,14 +29,19 @@ return {
Textures = {
Type = "simple",
Color = "textures/neptune.jpg",
},
}
},
Translation = {
Tag = "planet_solarSystem",
Transform = {
Rotation = {
Type = "SpiceRotation",
SourceFrame = "IAU_NEPTUNE",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1,
},
}
},
-- NeptuneTrail module
@@ -52,7 +57,8 @@ return {
},
Color = {0.2, 0.5, 1.0 },
Period = 60200,
Resolution = 1000
},
Resolution = 1000,
Tag = "planetTrail_solarSystem"
}
}
}

View File

@@ -39,6 +39,7 @@ return {
MieColor = {1.0, 1.0, 1.0}
}
},
Tag = "planet_solarSystem",
Transform = {
Rotation = {
Type = "SpiceRotation",
@@ -76,7 +77,8 @@ return {
},
Color = {0.85,0.75,0.51 },
Period = 10746.94,
Resolution = 1000
Resolution = 1000,
Tag = "planetTrail_solarSystem"
},
}
}

View File

@@ -36,12 +36,17 @@ return {
MieColor = {1.0, 1.0, 1.0}
}
},
Tag = "planet_solarSystem",
Transform = {
Rotation = {
Type = "SpiceRotation",
SourceFrame = "IAU_URANUS",
DestinationFrame = "ECLIPJ2000",
}
},
Scale = {
Type = "StaticScale",
Scale = 1,
},
},
},
@@ -58,7 +63,8 @@ return {
},
Color = {0.60, 0.95, 1.00 },
Period = 30588.740,
Resolution = 1000
},
Resolution = 1000,
Tag = "planetTrail_solarSystem"
}
}
}

View File

@@ -36,10 +36,17 @@ return {
MieColor = {1.0, 1.0, 1.0}
}
},
Rotation = {
Type = "SpiceRotation",
Frame = "IAU_VENUS",
Reference = "GALACTIC"
Tag = {"planet_solarSystem", "planet_terrestrial"},
Transform = {
Rotation = {
Type = "SpiceRotation",
SourceFrame = "IAU_VENUS",
DestinationFrame = "GALACTIC"
},
Scale = {
Type = "StaticScale",
Scale = 1,
},
},
},
@@ -56,7 +63,8 @@ return {
},
Color = { 1.0, 0.5, 0.2 },
Period = 224.695,
Resolution = 1000
Resolution = 1000,
Tag = {"planetTrail_solarSystem", "planetTrail_terrestrial"}
}
}
}

View File

@@ -207,6 +207,20 @@ public:
/// \see PropertyOwner::removePropertySubOwner(PropertyOwner*)
void removePropertySubOwner(PropertyOwner& owner);
/**
* Returns a list of all tags that have been assigned to the Property. Useful for
* trying to find a match for a desired batch operation on Properties.
* \return Pointer to vector of string tags that were assigned to the Property
*/
std::vector<std::string> tags() const;
/**
* Adds a tag to the Property's list of assigned tags. Tags are useful for creating
* groups of Properties that can be used in batch operations.
* \param tag The string that is to be assigned to the Property
*/
void addTag(std::string tag);
private:
/// The name of this PropertyOwner
std::string _name;
@@ -218,6 +232,8 @@ private:
std::vector<PropertyOwner*> _subOwners;
/// The associations between group identifiers of Property's and human-readable names
std::map<std::string, std::string> _groupNames;
/// Collection of string tag(s) assigned to this property
std::vector<std::string> _tags;
};
} // namespace properties

View File

@@ -61,6 +61,7 @@ public:
static const std::string KeyName;
static const std::string KeyParentName;
static const std::string KeyDependencies;
static const std::string KeyTag;
SceneGraphNode();
~SceneGraphNode();

View File

@@ -324,5 +324,13 @@ const std::string& PropertyOwner::name() const {
return _name;
}
std::vector<std::string> PropertyOwner::tags() const {
return _tags;
}
void PropertyOwner::addTag(std::string tag) {
_tags.push_back(std::move(tag));
}
} // namespace properties
} // namespace openspace

View File

@@ -41,6 +41,7 @@ namespace {
const char* keyStart = "StartTime";
const char* keyEnd = "EndTime";
const char* KeyType = "Type";
const char* KeyTag = "Tag";
}
namespace openspace {
@@ -112,6 +113,21 @@ Renderable::Renderable(const ghoul::Dictionary& dictionary)
dictionary.getValue(keyStart, _startTime);
dictionary.getValue(keyEnd, _endTime);
if (dictionary.hasKeyAndValue<std::string>(KeyTag)) {
std::string tagName = dictionary.value<std::string>(KeyTag);
if (!tagName.empty())
addTag(std::move(tagName));
} else if (dictionary.hasKeyAndValue<ghoul::Dictionary>(KeyTag)) {
ghoul::Dictionary tagNames = dictionary.value<ghoul::Dictionary>(KeyTag);
std::vector<std::string> keys = tagNames.keys();
std::string tagName;
for (const std::string& key : keys) {
tagName = tagNames.value<std::string>(key);
if (!tagName.empty())
addTag(std::move(tagName));
}
}
if (_startTime != "" && _endTime != "") {
_hasTimeInterval = true;
}

View File

@@ -47,7 +47,7 @@ namespace {
const char* KeyScale = "Scale";
const char* KeyDepth = "Depth";
const char* KeyAlpha = "Alpha";
const char* KeyTag = "Tag";
const float PlaneDepth = -2.f;
}
@@ -146,6 +146,21 @@ ScreenSpaceRenderable::ScreenSpaceRenderable(const ghoul::Dictionary& dictionary
dictionary.getValue(KeyDepth, _depth);
dictionary.getValue(KeyAlpha, _alpha);
if (dictionary.hasKeyAndValue<std::string>(KeyTag)) {
std::string tagName = dictionary.value<std::string>(KeyTag);
if (!tagName.empty())
addTag(std::move(tagName));
} else if (dictionary.hasKeyAndValue<ghoul::Dictionary>(KeyTag)) {
ghoul::Dictionary tagNames = dictionary.value<ghoul::Dictionary>(KeyTag);
std::vector<std::string> keys = tagNames.keys();
std::string tagName;
for (const std::string& key : keys) {
tagName = tagNames.value<std::string>(key);
if (!tagName.empty())
addTag(std::move(tagName));
}
}
// Setting spherical/euclidean onchange handler
_useFlatScreen.onChange([this](){
if (_useFlatScreen) {

View File

@@ -448,26 +448,38 @@ scripting::LuaLibrary Scene::luaLibrary() {
"setPropertyValue",
&luascriptfunctions::property_setValue,
"string, *",
"Sets all properties identified by the URI (with potential wildcards) in "
"the first argument. The second argument can be any type, but it has to "
"match the type that the property (or properties) expect."
"Sets all property(s) identified by the URI (with potential wildcards) "
"in the first argument. The second argument can be any type, but it has "
"to match the type that the property (or properties) expect. If the "
"first term (separated by '.') in the uri is bracketed with { }, then "
"this term is treated as a group tag name, and the function will "
"search through all property owners to find those that are tagged with "
"this group name, and set their property values accordingly."
},
{
"setPropertyValueRegex",
&luascriptfunctions::property_setValueRegex,
"string, *",
"Sets all properties that pass the regular expression in the first "
"argument. The second argument can be any type, but it has to match the "
"type of the properties that matched the regular expression. The regular "
"expression has to be of the ECMAScript grammar."
"Sets all property(s) that pass the regular expression in the first "
"argument. The second argument can be any type, but it has to match "
"the type of the properties that matched the regular expression. "
"The regular expression has to be of the ECMAScript grammar. If the "
"first term (separated by '.') in the uri is bracketed with { }, then "
"this term is treated as a group tag name, and the function will search "
"through all property owners to find those that are tagged with this "
"group name, and set their property values accordingly."
},
{
"setPropertyValueSingle",
&luascriptfunctions::property_setValueSingle,
"string, *",
"Sets a property identified by the URI in "
"the first argument. The second argument can be any type, but it has to "
"match the type that the property expects.",
"Sets all property(s) identified by the URI in the first argument to the "
"value passed in the second argument. The type of the second argument is "
"arbitrary, but it must agree with the type the denoted Property expects."
" If the first term (separated by '.') in the uri is bracketed with { }, "
" then this term is treated as a group tag name, and the function will "
"search through all property owners to find those that are tagged with "
"this group name, and set their property values accordingly."
},
{
"getPropertyValue",

View File

@@ -28,17 +28,67 @@ namespace openspace {
namespace {
void applyRegularExpression(lua_State* L, std::regex regex, std::vector<properties::Property*> properties, int type) {
void executePropertySet(properties::Property* prop, lua_State* L) {
prop->setLuaValue(L);
//ensure properties are synced over parallel connection
std::string value;
prop->getStringValue(value);
/*OsEng.parallelConnection().scriptMessage(
prop->fullyQualifiedIdentifier(),
value
);*/
}
template <class T>
properties::PropertyOwner* findPropertyOwnerWithMatchingGroupTag(T* prop,
const std::string& tagToMatch)
{
properties::PropertyOwner* tagMatchOwner = nullptr;
properties::PropertyOwner* owner = prop->owner();
if (owner) {
std::vector<std::string> tags = owner->tags();
for (std::string& currTag : tags) {
if (tagToMatch.compare(currTag) == 0) {
tagMatchOwner = owner;
break;
}
}
//Call recursively until we find an owner with matching tag or the top of the
// ownership list
if (tagMatchOwner == nullptr)
tagMatchOwner = findPropertyOwnerWithMatchingGroupTag(owner, tagToMatch);
}
return tagMatchOwner;
}
void applyRegularExpression(lua_State* L, std::regex regex,
std::vector<properties::Property*> properties, int type,
std::string groupName = "")
{
using ghoul::lua::errorLocation;
using ghoul::lua::luaTypeToString;
bool isGroupMode = !groupName.empty();
for (properties::Property* prop : properties) {
// Check the regular expression for all properties
std::string id = prop->fullyQualifiedIdentifier();
if (std::regex_match(id, regex)) {
// If the fully qualified id matches the regular expression, we queue the
// value change if the types agree
if (isGroupMode) {
properties::PropertyOwner* matchingTaggedOwner =
findPropertyOwnerWithMatchingGroupTag(
prop,
groupName
);
if (!matchingTaggedOwner) {
continue;
}
}
if (type != prop->typeLua()) {
LERRORC("property_setValue",
errorLocation(L) << "Property '" <<
@@ -46,55 +96,50 @@ void applyRegularExpression(lua_State* L, std::regex regex, std::vector<properti
"' does not accept input of type '" << luaTypeToString(type) <<
"'. Requested type: '" << luaTypeToString(prop->typeLua()) << "'"
);
} else {
executePropertySet(prop, L);
}
else {
prop->setLuaValue(L);
//ensure properties are synced over parallel connection
std::string value;
prop->getStringValue(value);
/* OsEng.parallelConnection().scriptMessage(
prop->fullyQualifiedIdentifier(),
value
);*/
}
}
}
}
//Checks to see if URI contains a group tag (with { } around the first term). If so,
// returns true and sets groupName with the tag
bool doesUriContainGroupTag(const std::string& command, std::string& groupName) {
std::string name = command.substr(0, command.find_first_of("."));
if (name.front() == '{' && name.back() == '}') {
groupName = name.substr(1, name.length() - 2);
return true;
} else {
return false;
}
}
std::string replaceUriWithGroupName(std::string uri, std::string ownerName) {
size_t pos = uri.find_first_of(".");
return ownerName + "." + uri.substr(pos);
}
std::string extractUriWithoutGroupName(std::string uri) {
size_t pos = uri.find_first_of(".");
return uri.substr(pos);
}
}
namespace luascriptfunctions {
/**
* \ingroup LuaScripts
* setPropertyValueSingle(string, *):
* Sets the property identified by the URI in the first argument to the value passed to
* the second argument. The type of the second argument is arbitrary, but it must agree
* with the type the denoted Property expects
*/
int property_setValueSingle(lua_State* L) {
int setPropertyCall_single(properties::Property* prop, std::string uri, lua_State* L,
const int type)
{
using ghoul::lua::errorLocation;
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
SCRIPT_CHECK_ARGUMENTS("property_setValueSingle", L, 2, nArguments);
std::string uri = luaL_checkstring(L, -2);
const int type = lua_type(L, -1);
properties::Property* prop = property(uri);
if (!prop) {
LERRORC("property_setValue", errorLocation(L) << "Property with URI '" << uri << "' was not found");
return 0;
}
if (type != prop->typeLua()) {
LERRORC("property_setValue", errorLocation(L) << "Property '" << uri <<
"' does not accept input of type '" << luaTypeToString(type) <<
"'. Requested type: '" << luaTypeToString(prop->typeLua()) << "'");
return 0;
}
else {
prop->setLuaValue(L);
@@ -107,12 +152,116 @@ int property_setValueSingle(lua_State* L) {
return 0;
}
/**
* \ingroup LuaScripts
* setPropertyValueSingle(string, *):
* Sets all property(s) identified by the URI in the first argument to the value passed
* in the second argument. The type of the second argument is arbitrary, but it must
* agree with the type the denoted Property expects.
* If the first term (separated by '.') in the uri is bracketed with { }, then this
* term is treated as a group tag name, and the function will search through all
* property owners to find those that are tagged with this group name, and set their
* property values accordingly.
*/
int property_setValueSingle(lua_State* L) {
using ghoul::lua::errorLocation;
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
SCRIPT_CHECK_ARGUMENTS("property_setValueSingle", L, 2, nArguments);
std::string uri = luaL_checkstring(L, -2);
const int type = lua_type(L, -1);
std::string tagToMatch;
if (doesUriContainGroupTag(uri, tagToMatch)) {
std::string pathRemainderToMatch = extractUriWithoutGroupName(uri);
for (properties::Property* prop : allProperties()) {
std::string propFullId = prop->fullyQualifiedIdentifier();
//Look for a match in the uri with the group name (first term) removed
int propMatchLength = propFullId.length() - pathRemainderToMatch.length();
if (propMatchLength >= 0) {
std::string thisPropMatchId = propFullId.substr(propMatchLength);
//If remainder of uri matches (with group name removed),
if (pathRemainderToMatch.compare(thisPropMatchId) == 0) {
properties::PropertyOwner* matchingTaggedOwner
= findPropertyOwnerWithMatchingGroupTag(prop, tagToMatch);
if (matchingTaggedOwner) {
setPropertyCall_single(prop, uri, L, type);
}
}
}
}
} else {
properties::Property* prop = property(uri);
if (!prop) {
LERRORC("property_setValue", errorLocation(L) << "Property with URI '"
<< uri << "' was not found");
return 0;
}
setPropertyCall_single(prop, uri, L, type);
}
return 0;
}
/**
* \ingroup LuaScripts
* setPropertyValue(string, *):
* Sets all property(s) identified by the URI (with potential wildcards) in the first
* argument. The second argument can be any type, but it has to match the type that the
* property (or properties) expect.
* If the first term (separated by '.') in the uri is bracketed with { }, then this
* term is treated as a group tag name, and the function will search through all
* property owners to find those that are tagged with this group name, and set their
* property values accordingly.
*/
int property_setValue(lua_State* L) {
using ghoul::lua::errorLocation;
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
SCRIPT_CHECK_ARGUMENTS("property_setGroup", L, 2, nArguments);
std::string regex = luaL_checkstring(L, -2);
std::string groupName;
// Replace all wildcards * with the correct regex (.*)
size_t startPos = regex.find("*");
while (startPos != std::string::npos) {
regex.replace(startPos, 1, "(.*)");
startPos += 4;
startPos = regex.find("*", startPos);
}
if (doesUriContainGroupTag(regex, groupName)) {
std::string pathRemainderToMatch = extractUriWithoutGroupName(regex);
//Remove group name from start of regex and replace with '.*'
regex = replaceUriWithGroupName(regex, ".*");
}
applyRegularExpression(
L,
std::regex(regex/*, std::regex_constants::optimize*/),
allProperties(),
lua_type(L, -1),
groupName
);
return 0;
}
/**
* \ingroup LuaScripts
* setPropertyValueRegex(string, *):
* Sets all properties that pass the regular expression in the first argument. The second
* Sets all property(s) that pass the regular expression in the first argument. The second
* argument can be any type, but it has to match the type of the properties that matched
* the regular expression. The regular expression has to be of the ECMAScript grammar.
* If the first term (separated by '.') in the uri is bracketed with { }, then this
* term is treated as a group tag name, and the function will search through all
* property owners to find those that are tagged with this group name, and set their
* property values accordingly.
*/
int property_setValueRegex(lua_State* L) {
using ghoul::lua::errorLocation;
@@ -120,63 +269,32 @@ int property_setValueRegex(lua_State* L) {
int nArguments = lua_gettop(L);
SCRIPT_CHECK_ARGUMENTS("property_setValueRegex<", L, 2, nArguments);
std::string regex = luaL_checkstring(L, -2);
std::string groupName;
if (doesUriContainGroupTag(regex, groupName)) {
std::string pathRemainderToMatch = extractUriWithoutGroupName(regex);
//Remove group name from start of regex and replace with '.*'
regex = replaceUriWithGroupName(regex, ".*");
}
try {
applyRegularExpression(
L,
std::regex(regex, std::regex_constants::optimize),
allProperties(),
lua_type(L, -1)
lua_type(L, -1),
groupName
);
}
catch (const std::regex_error&) {
LERRORC(
"property_setValueRegex",
"Malformed regular expression: '" << regex << "'"
);
catch (const std::regex_error& e) {
LERRORC("property_setValueRegex", "Malformed regular expression: '"
<< regex << "'");
}
return 0;
}
/**
* \ingroup LuaScripts
* setPropertyValue(string, *):
* Sets all properties identified by the URI (with potential wildcards) in the first
* argument. The second argument can be any type, but it has to match the type that the
* property (or properties) expect.
*/
int property_setValue(lua_State* L) {
using ghoul::lua::errorLocation;
using ghoul::lua::luaTypeToString;
int nArguments = lua_gettop(L);
SCRIPT_CHECK_ARGUMENTS("property_setValue", L, 2, nArguments);
std::string regex = luaL_checkstring(L, -2);
// Replace all wildcards * with the correct regex (.*)
size_t startPos = regex.find("*");
while (startPos != std::string::npos) {
regex.replace(startPos, 1, "(.*)");
startPos += 4;
startPos = regex.find("*", startPos);
}
applyRegularExpression(
L,
std::regex(regex/*, std::regex_constants::optimize*/),
allProperties(),
lua_type(L, -1)
);
return 0;
}
/**
* \ingroup LuaScripts
* getPropertyValue(string):

View File

@@ -64,6 +64,7 @@ const std::string SceneGraphNode::RootNodeName = "Root";
const std::string SceneGraphNode::KeyName = "Name";
const std::string SceneGraphNode::KeyParentName = "Parent";
const std::string SceneGraphNode::KeyDependencies = "Dependencies";
const std::string SceneGraphNode::KeyTag = "Tag";
std::unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(const ghoul::Dictionary& dictionary){
openspace::documentation::testSpecificationAndThrow(
@@ -135,6 +136,25 @@ std::unique_ptr<SceneGraphNode> SceneGraphNode::createFromDictionary(const ghoul
LDEBUG("Successfully created scale for '" << result->name() << "'");
}
if (dictionary.hasKey(KeyTag)) {
if (dictionary.hasKeyAndValue<std::string>(KeyTag)) {
std::string tagName = dictionary.value<std::string>(KeyTag);
if (!tagName.empty()) {
result->addTag(std::move(tagName));
}
} else if (dictionary.hasKeyAndValue<ghoul::Dictionary>(KeyTag)) {
ghoul::Dictionary tagNames = dictionary.value<ghoul::Dictionary>(KeyTag);
std::vector<std::string> keys = tagNames.keys();
std::string tagName;
for (const std::string& key : keys) {
tagName = tagNames.value<std::string>(key);
if (!tagName.empty()) {
result->addTag(std::move(tagName));
}
}
}
}
LDEBUG("Successfully created SceneGraphNode '"
<< result->name() << "'");
return std::move(result);