diff --git a/include/openspace/scene/scene.h b/include/openspace/scene/scene.h index a7f6cba489..fa0d25cb3e 100644 --- a/include/openspace/scene/scene.h +++ b/include/openspace/scene/scene.h @@ -54,6 +54,7 @@ enum class PropertyValueType { Table, Nil }; +typedef std::variant ProfilePropertyLua; class SceneInitializer; @@ -255,6 +256,60 @@ public: void setPropertiesFromProfile(const Profile& p); private: + /** + * Accepts string version of a property value from a profile, converts it to the + * appropriate type, and then pushes the value onto the lua state. + * + * \param L the lua state to push value to + * \param value string representation of the value with which to set property + */ + void propertyPushValueFromProfileToLuaState(ghoul::lua::LuaState& L, + std::string& value); + + /** + * Accepts string version of a property value from a profile, and processes it + * according to the data type of the value + * + * \param L the lua state to (eventually) push to + * \param value string representation of the value with which to set property + * \param didPushToLua Bool reference that represents the lua push state at the end + * of this function call. This will be set to true if the value (e.g. table) + * has already been pushed to the lua stack + * \return The ProfilePropertyLua variant type translated from string representation + */ + ProfilePropertyLua propertyProcessValue(ghoul::lua::LuaState& L, + std::string& value, bool& didPushToLua); + + /** + * Accepts string version of a property value from a profile, and returns the + * supported data types that can be pushed to a lua state. Currently, the full + * range of possible lua values is not supported. + * + * \param value string representation of the value with which to set property + */ + PropertyValueType getPropertyValueType(std::string& value); + + /** + * Accepts string version of a property value from a profile, and adds it to a vector + * which will later be used to push as a lua table containing values of type T + * + * \param L the lua state to (eventually) push to + * \param value string representation of the value with which to set property + * \param table the std::vector container which has elements of type T for a lua table + */ + template + void processPropertyValueTableEntries(ghoul::lua::LuaState& L, std::string& value, + std::vector& table); + + /** + * Handles a lua table entry, creating a vector of the correct variable type based + * on the profile string, and pushes this vector to the lua stack. + * + * \param L the lua state to (eventually) push to + * \param value string representation of the value with which to set property + */ + void handlePropertyLuaTableEntry(ghoul::lua::LuaState& L, std::string& value); + /** * Update dependencies. */ @@ -269,7 +324,7 @@ private: bool _dirtyNodeRegistry = false; SceneGraphNode _rootDummy; std::unique_ptr _initializer; - + std::string _profilePropertyName; std::vector _interestingTimes; std::mutex _programUpdateLock; @@ -288,49 +343,7 @@ private: ghoul::MemoryPool<4096> _memoryPool; }; -/** - * Accepts string version of a property value from a profile, converts it to the - * appropriate type, and then pushes the value onto the lua state. - * - * \param L the lua state to push value to - * \param value string representation of the value with which to set property - */ -void propertyPushValueFromProfileToLuaState(ghoul::lua::LuaState& L, - const std::string& value); - -/** - * Accepts string version of a property value from a profile, and processes it - * according to the data type of the value - * - * \param L the lua state to (eventually) push to - * \param value string representation of the value with which to set property - * \param pushFn the std::function provided for pushing the result, which may be - * a lambda that pushes to the lua state, or a lambda that pushes - * to a vector (the vector will eventually be pushed to a lua table) - */ -void propertyProcessValue(ghoul::lua::LuaState& L, std::string& value, - std::functionpushFn); - -/** - * Accepts string version of a property value from a profile, and returns the - * supported data types that can be pushed to a lua state. Currently, the full - * range of possible lua values is not supported. - * - * \param value string representation of the value with which to set property - */ -PropertyValueType getPropertyValueType(std::string& value); - -/** - * Accepts string version of a property value from a profile, and adds it to a vector - * which will later be used to push as a lua table containing values of type T - * - * \param L the lua state to (eventually) push to - * \param value string representation of the value with which to set property - * \param table the std::vector container which has elements of type T for a lua table - */ -template -void processPropertyValueTableEntries(ghoul::lua::LuaState& L, std::string& value, - std::vector& table); +void trimSurroundingCharacters(std::string& valueString, const char c); } // namespace openspace diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 12833bade7..5f4073c5f3 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -73,6 +73,9 @@ namespace { } } #endif // TRACY_ENABLE + + template struct overloaded : Ts... { using Ts::operator()...; }; + template overloaded(Ts...)->overloaded; } // namespace namespace openspace { @@ -613,10 +616,14 @@ void Scene::setPropertiesFromProfile(const Profile& p) { // Remove group name from start of regex and replace with '*' uriOrRegex = removeGroupNameFromUri(uriOrRegex); } + _profilePropertyName = uriOrRegex; ghoul::lua::push(L, uriOrRegex); ghoul::lua::push(L, 0.0); + + std::string workingValue = prop.value; + trimSurroundingCharacters(workingValue, ' '); // Later functions expect the value to be at the last position on the stack - propertyPushValueFromProfileToLuaState(L, prop.value); + propertyPushValueFromProfileToLuaState(L, workingValue); applyRegularExpression( L, @@ -631,114 +638,145 @@ void Scene::setPropertiesFromProfile(const Profile& p) { } } -template -void propertyPushValueFromProfileToLuaState(ghoul::lua::LuaState& L, - std::string& value) +void Scene::propertyPushValueFromProfileToLuaState(ghoul::lua::LuaState& L, + std::string& value) { - propertyProcessValue( - L, - value, - [&](T resultValue) { - ghoul::lua::push(L, resultValue); - } - ); + bool alreadyPushedToLua = false; + ProfilePropertyLua elem = propertyProcessValue(L, value, alreadyPushedToLua); + if (!alreadyPushedToLua) { + std::visit(overloaded{ + [&L](const bool& value) { + ghoul::lua::push(L, value); + }, + [&L](const float& value) { + ghoul::lua::push(L, value); + }, + [&L](const std::string& value) { + ghoul::lua::push(L, value); + }, + [&L](const ghoul::lua::nil_t& nilValue) { + ghoul::lua::push(L, nilValue); + } + }, elem); + } } -template -void propertyProcessValue(ghoul::lua::LuaState& L, std::string& value, - std::functionpushFn) +ProfilePropertyLua Scene::propertyProcessValue(ghoul::lua::LuaState& L,std::string& value, + bool& didPushToLua) { + ProfilePropertyLua result; PropertyValueType pType = getPropertyValueType(value); switch (pType) { case PropertyValueType::Boolean: - (value == "true") ? pushFn(true) : pushFn(false); + result = (value == "true") ? true : false; break; case PropertyValueType::Float: - pushFn(L, std::stof(value)); + result = std::stof(value); break; case PropertyValueType::Nil: ghoul::lua::nil_t n; - pushFn(n); + result = n; break; case PropertyValueType::Table: - std::string nextValue = value; - size_t commaPos = value.find(',', commaPos); - if (commaPos != std::string::npos) { - nextValue = value.substr(1, commaPos); - } - PropertyValueType enclosedType = getPropertyValueType(nextValue); - switch (enclosedType) { - case PropertyValueType::Boolean: - std::vector valsB; - processPropertyValueTableEntries(L, value, valsB); - pushFn(valsB); - break; + trimSurroundingCharacters(value, '{'); + trimSurroundingCharacters(value, '}'); + handlePropertyLuaTableEntry(L, value); + didPushToLua = true; + break; - case PropertyValueType::Float: + case PropertyValueType::String: + default: + std::string newValue = value; + newValue.insert(0, "[["); + newValue.append("]]"); + result = newValue; + break; + } + return result; +} + +void Scene::handlePropertyLuaTableEntry(ghoul::lua::LuaState& L, std::string& value) { + std::string firstValue; + size_t commaPos = 0; + commaPos = value.find(',', commaPos); + if (commaPos != std::string::npos) { + firstValue = value.substr(0, commaPos); + } + else { + firstValue = value; + } + + PropertyValueType enclosedType = getPropertyValueType(firstValue); + switch (enclosedType) { + case PropertyValueType::Boolean: + LERROR(fmt::format( + "A lua table of bool values is not supported. (processing property {})", + _profilePropertyName) + ); + break; + + case PropertyValueType::Float: + { std::vector valsF; processPropertyValueTableEntries(L, value, valsF); - pushFn(valsF); - break; - - case PropertyValueType::Nil: - std::vector valsN; - processPropertyValueTableEntries(L, value, valsN); - pushFn(valsN); - break; - - case PropertyValueType::String: - std::vector valsS; - processPropertyValueTableEntries(L, value, valsS); - pushFn(valsS); - break; - - case PropertyValueType::Table: - default: - LERROR("Table-within-a-table values are not supported for profile property"); - break; + ghoul::lua::push(L, valsF); } break; + case PropertyValueType::String: + { + std::vector valsS; + processPropertyValueTableEntries(L, value, valsS); + ghoul::lua::push(L, valsS); + } + break; + + case PropertyValueType::Table: default: - value.insert(0, "[["); - value.append("]]"); - pushFn(value); + LERROR(fmt::format( + "Table-within-a-table values are not supported for profile a " + "property (processing property {})", _profilePropertyName) + ); break; } } template -void processPropertyValueTableEntries(ghoul::lua::LuaState& L, std::string& value, +void Scene::processPropertyValueTableEntries(ghoul::lua::LuaState& L, std::string& value, std::vector& table) { size_t commaPos = 0; size_t prevPos = 0; std::string nextValue; while (commaPos != std::string::npos) { - commaPos = value.find(',', commaPos); + commaPos = value.find(',', prevPos); if (commaPos != std::string::npos) { - nextValue = value.substr(prevPos, commaPos); + nextValue = value.substr(prevPos, commaPos - prevPos); prevPos = commaPos + 1; } else { nextValue = value.substr(prevPos); } - propertyProcessValue(L, nextValue, [&](T val){table.push_back(val);}); + trimSurroundingCharacters(nextValue, ' '); + bool alreadyPushedToLua = false; + ProfilePropertyLua tableElement = propertyProcessValue(L, nextValue, + alreadyPushedToLua); + try { + table.push_back(std::get(tableElement)); + } + catch (std::bad_variant_access& e) { + LERROR(fmt::format( + "Error attempting to parse profile property setting for " + "{} using value = {}", _profilePropertyName, value) + ); + } } } -PropertyValueType getPropertyValueType(std::string& value) { - //First trim spaces from front and back of string - while (value.front() == ' ') { - value.erase(0, 1); - } - while (value.back() == ' ') { - value.pop_back(); - } - +PropertyValueType Scene::getPropertyValueType(std::string& value) { if (luascriptfunctions::isBoolValue(value)) { return PropertyValueType::Boolean; } @@ -756,6 +794,15 @@ PropertyValueType getPropertyValueType(std::string& value) { } } +void trimSurroundingCharacters(std::string& valueString, const char c) { + while (valueString.front() == c) { + valueString.erase(0, 1); + } + while (valueString.back() == c) { + valueString.pop_back(); + } +} + scripting::LuaLibrary Scene::luaLibrary() { return { "", diff --git a/src/scene/scene_lua.inl b/src/scene/scene_lua.inl index 721394187f..32a8d9327d 100644 --- a/src/scene/scene_lua.inl +++ b/src/scene/scene_lua.inl @@ -915,8 +915,14 @@ bool isBoolValue(const std::string& s) { */ bool isFloatValue(const std::string& s) { try { - float converted = std::stof(s); - return true; + float converted = std::numeric_limits::min(); + converted = std::stof(s); + if (converted != std::numeric_limits::min()) { + return true; + } + else { + return false; + } } catch (...) { return false;